There's something quite non-obvious going on in this code:
float a = 1.;
const float & x = true ? a : 2.; // Note: `2.` is a double
a = 4.;
std::cout << a << ", " << x;
both clang and gcc output:
4, 1
One would naively expect the same value printed twice but this isn't the case. The issue here has nothing to do with the reference. There are some interesting rules dictating the type of ? :
. If the two arguments are of different type and can be casted, they will by using a temporary. The reference will point to the temporary of ? :
.
The example above compiles fine and it might or might not issue a warning while compiling with -Wall
depending on the version of your compiler.
Here's an example on how easy it's to get this wrong in legitimate-looking code:
template<class Iterator, class T>
const T & min(const Iterator & iter, const T & b)
{
return *iter < b ? *iter : b;
}
int main()
{
// Try to remove the const or convert to vector of floats
const std::vector<double> a(1, 3.0);
const double & result = min(a.begin(), 4.);
cout << &a[0] << ", " << &result;
}
If your logic after this code assumes that any changes on a[0]
will be reflected to result
, it will be wrong in cases where ?:
creates a temporary. Also, if at some point you make a pointer to result
and you use it after result
goes out of scope, there will be a segmentation fault despite the fact that your original a
hasn't gone out of scope.
I feel there're serious reasons NOT to use this form beyond "maintainability and reading issues" mentioned here especially while writing templated code where some of your types and their const'ness might be out of your control.
So my question is, is it safe to use const &
s on ternary operators?
P.S. Bonus example 1, extra complications (see also here):
float a = 0;
const float b = 0;
const float & x = true ? a : b;
a = 4;
cout << a << ", " << x;
clang output:
4, 4
gcc 4.9.3 output:
4, 0
With clang this example compiles and runs as expected but with up to recent versions of gcc (
P.S.2 Bonus example 2, great for interviews ;) :
double a = 3;
const double & a_ref = a;
const double & x = true ? a_ref : 2.;
a = 4.;
std::cout << a << ", " << x;
output:
4, 3