Is the following code legal (by C++11 and/or C++14 standard(s))?
#include <iostream>
#include <utility>
using namespace std;
void foo(int &a) {
cout << a << endl;
}
int main() {
foo(reinterpret_cast<int &>(move(5)));
}
a
inside foo
without it becoming UB?It compiles on clang 3.5, not on gcc 4.9. GCC error:
➤ g++-4.9 -std=c++1y sample.cpp -o sample
sample.cpp: In function 'int main()':
sample.cpp:11:40: error: invalid cast of an rvalue expression of type 'std::remove_reference<int>::type {aka int}' to type 'int&'
foo(reinterpret_cast<int &>(move(5)));
^
FYI, a custom made cast that's less hairy than the previous, and which works on C++11 for both GCC and Clang, would be the following lvalue
function:
#include <iostream>
namespace non_std {
template <typename T>
constexpr T &lvalue(T &&r) noexcept { return r; }
}
void divs(int &a, int &b) {
int t = a;
a /= b;
b /= t;
}
int main() {
using namespace std;
using namespace non_std;
int i_care_for_this_one = 4;
divs(i_care_for_this_one, lvalue(2));
cout << i_care_for_this_one << endl;
}
Update: The code is ill-formed in C++11. Answer below is for C++14. See note at the end of this answer.
I believe this code is both well-formed and well-defined. Here's why.
The result of std::move
is an xvalue [1], which is a type of glvalue; and converting a glvalue to an lvalue reference with reinterpret_cast
appears to be allowed by the wording of the standard:
A glvalue expression of type
T1
can be cast to the type “reference toT2
” if an expression of type “pointer toT1
” can be explicitly converted to the type “pointer toT2
” using areinterpret_cast
. The result refers to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference castreinterpret_cast<T&>(x)
has the same effect as the conversion*reinterpret_cast<T*>(&x)
with the built-in&
and*
operators (and similarly forreinterpret_cast<T&&>(x)
). — end note ] No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.73
Since "pointer to int
" can be converted to "pointer to int
", this reinterpret_cast
is also allowed. The standard doesn't say anything about whether the destination type has to be an lvalue reference or rvalue reference.
The result of the cast is well-defined by the paragraph above: it refers to the same object as the source glvalue---that is, a temporary int
object with the value 5
. ([dcl.init.ref] specifies that a temporary is created when a prvalue is bound to a reference.)
Accessing the value through the int&
also doesn't violate any aliasing rules since the original object was also of type int
. In fact I believe it would even be well-defined to modify the temporary through the lvalue thus obtained.
Note: The C++11 wording says "lvalue expression", not "glvalue expression". The wording with "glvalue expression" is from N3936, which is the final working draft for C++14. I'm not an expert in how the standardization process works, but I believe this means that the change of "lvalue" to "glvalue" was already voted in by the committee, and when ISO publishes the C++14 standard, it's going to be pretty similar to what it says above.
[1] Except in the rare case in which the argument is a function; in that case the result is an lvalue, since there are no function rvalues.