A common optimization scenario
A common problem with the ternary operator involves trying to optimizate an assigned like this:
1
2
3
4
5
6
7
#include <string>
std::string GetPossibleString();
void DoStuff() {
std::string possible_string = GetPossibleString();
auto string_to_use = possible_string.empty() ? "Default"
: possible_string;
}
What is the type of string_to_use
? Well, it is of type std::string
as there
is an implicit conversion from the const char*
C-style string "Default"
to
a std::string
.
Now, this might be fine, and expected, but following the latest guidance from
the C++ standard library, you may see more and more interfaces flexible enough
to take std::string_view
, which is a lightweight, readonly view of a
“string”, which might be a std::string
, or const char*
. Ownership of the
string is not taken, as the name suggests: it’s just a view of the string.
Then you might say, well, I do not want to incurr the expense of dynamic memory
allocation and copying "Default"
to a std::string
, so, knowing that both
possible_string
and "Default"
are implicitly convertible to a
std::string_view
, I’ll modify the code as follows:
1
2
3
4
5
6
7
8
#include <string>
#include <string_view>
std::string GetPossibleString();
void DoStuff() {
std::string possible_string = GetPossibleString();
std::string_view string_to_use = possible_string.empty() ? "Default"
: possible_string;
}
However, you’ve now inadvertently entered the world of undefined behavior.
The reason is because the ternary operator doesn’t really care what the type of
string_to_use
is. It’s going to resolve a common type between the two
branches possible involving an implicit conversion.
In this case, there is only one thing it can do: the implicit conversion of
"Default"
to a std::string
. After that, either that temporary string, or
possible_string
is converted to a std::string_view
. Needless to say, a
std::string_view
pointing at a temporary value is bad, since this will elicit
the same undefinied behavior of taking the address of a temporary and using it
after the statement the temporary is in.
So, how to fix this? There should be a way to do this because we rightly
should be able to assign the possible_string
and "Default"
to a
std::string_view
without problem.
Solution
The trick is, you need to explicitly make one of the branches of the ternary
operator to be a std::string_view
in one of its branches (or both). So this
is a possible solution:
1
2
3
4
5
6
7
8
9
#include <string>
#include <string_view>
std::string GetPossibleString();
void DoStuff() {
std::string possible_string = GetPossibleString();
std::string_view string_to_use =
possible_string.empty() ? "Default"
: std::string_view(possible_string);
}
The moral of the story is, implicit conversions do not always behave as you expect, particuarly in the presence of resolution in the ternary operator. Be very careful, and when in double, do explicitly conversions.