My question is in relation to this little code snippet:
typedef std::map<std::string, std::string> my_map_t;
std::string_view get_value_worse(const my_map_t& input_map, std::string_view value)
{
auto retrieved = input_map.find(value.data());
return retrieved != input_map.cend() ? retrieved->second : "";
}
std::string_view get_value_better(const my_map_t& input_map, std::string_view value)
{
auto retrieved = input_map.find(value.data());
if (retrieved != input_map.cend())
{
return retrieved->second;
}
return "";
}
int main()
{
my_map_t my_map = {
{"key_0", "value_0"},
{"key_1", "value_1"},
};
std::cout << (get_value_worse(my_map, "key_0") == get_value_better(my_map, "key_0")) << std::endl;
}
Under the latest gcc with no optimisations this prints 0 for false, while under -O3 this prints 1 for true.
I believe the un-optimised behaviour is because the second and third comparison operator arguments are expressions, not statements - and so the retrieved->second in retrieved != arguments.cend() ? retrieved->second : "" gets evaluated as a string construction on the stack, and returning a string_view to that is bad.
I can also see that with -O3 the compiler would be able to inline all of this, remove branching, and be done with it... but I would have expected -O3 to act exactly "as if" I had compiled with -O0.
Can anyone explain why the compiler gets to elide the copy construction I believe is happening in the -O0 version?
std::stringobject. Which, in both cases, goes out of scope and gets destroyed when either function returns, leaving you with a string_view of a deleted object. All further use of it is undefined behavior, and results in spontaneous demons flying out of everyone's nose.get_value_betterisn't undefined as its returning astring_viewfor astd::stringwhich still exists inmy_map?std::map::findpoints to the actual pair present in thestd::map. The there's no going out of scope inget_value_better, it's completely fine.