There's something quite non-obvious going on in this code:
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
You would naturally expect a and x to refer to the same thing since "reference is its referent" according to The C++ FAQ:
The reference itself isn't an object (it has no identity; taking the address of a reference gives you the address of the referent; remember: the reference is its referent).
This is true but what's actually the referent in this case? With clang this example indeed compiles and runs as expected but with up to recent versions of gcc (v5.3) you will get different numbers out of it (!) This other slight variation will always cause problems with both clang and gcc:
double a = 0;
const float b = 0;
const float & x = true ? a : b;
a = 4;
cout << a << ", " << x;
both clang and gcc output:
4, 0
All these have nothing to do with the reference. The problem is that there are some interesting rules on 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 value of ? :.
In the first case, there was a change between the rules in C++03 and C++11. In C++11 the const'ness "cast" will happen without a temporary because frankly... it can since you're moving to more strict const'ness! :) So you see clang working "as expected". Somewhat older but still extremely popular, at the time of writing, versions of gcc go with the C++03 definition even when they run on C++11 mode. Anyway, In the second case, there's always a temporary because double is casted to a float.
Both the above examples compile fine. There might or might not be a warning when compiling with -Wall depending on the version of the 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 the 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 keep 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 that there're serious reasons NOT to use this beyond "maintainability and reading issues" (see 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?
Aucun commentaire:
Enregistrer un commentaire