1

C++11 allows a rvalue reference to be declared in global(,namespace, class) scope and to have static storage duration. What would be a good scenario for using such variables? I have made several tests and have come up with a few observations. The following piece of code will not compile (g++ 5.3)

double&& m = 3.5;
double&& s() {return m;}

testy.cpp: In function ‘double&& s()’:

testy.cpp:7:22: error: cannot bind ‘double’ lvalue to ‘double&&’ double&& s() {return l;}

This is rather counterintuitive, even thought we declared a rvalue reference it is treated as if the data is returned by value. This is, of course, a contrived example and indeed returning a rvalue reference is rare. However why does it not work? My interpretation is that by declaring the rvalue reference m with static storage duration we get a form of "universal reference", a term coined by Scott Meyers (more info here). One of the properties of a universal reference is that it can bind to both lvalues and rvalues. However unlike "universal references", which rely on reference collapse due to type substitutions, the mechanism which is involved in the resolution above is unclear to me. In fact the following code compiles without problems.

double&& m = 3.5;

int main() {
    double a {4.2};
    double& b = a;
    m = a;
    m = b;
    m = 3.2;
}

Hence, as stated above, m binds to every flavor of double. However the fact that the variable has static storage duration seems to contradict the nature of rvalues, which bind to temporary object. There are few questions that arise. Why is that allowed? What scenario will make use of this property?. My first guess was that we can use that in providing template specializations which accept any "flavour" of argument, much the same way universal references do, with the exception that they are bound more strongly to the underlying type. Moreover the scope of the "static rvalue reference" seems not to matter, ie rvalues with static storage duration and "function" scope also exhibit the behavior as demonstrated below.

int main() {
    static double&& a = 4.2;
    double b = 3.2;
    double& c = b;
    a = b;
    a = c;
}
Teodor Nikolov
  • 783
  • 7
  • 14
  • 2
    id-expressions are always lvalues. The "rvalue" in "rvalue reference" refers to the kinds of value the reference can *bind to*. – Kerrek SB Mar 18 '16 at 13:41
  • Related question: https://stackoverflow.com/questions/35314093/passing-int-to-fint – Galik Mar 18 '16 at 13:47
  • Probably counterintuitive, but `rvalue reference` is an `lvalue` :) – SergeyA Mar 18 '16 at 13:48
  • 1
    I don't understand your last demonstration. You can't write `double& c = 1.2;` – Barry Mar 18 '16 at 13:49
  • @Barry apologies, it was a typo. – Teodor Nikolov Mar 18 '16 at 13:51
  • 1
    "In fact the following code compiles without problems. [...] Hence, as stated above, m binds to every flavor of double." Your example absolutely does not show that. `a = b;` and `a = c;` are assignments to the object to which `a` is bound. They don't re-bind the reference. –  Mar 18 '16 at 13:56
  • @KerrekSB Not all *id-expression*s are lvalues (e.g., enumerators). – T.C. Mar 18 '16 at 22:08
  • @T.C.: Yes, true. Would be good to come up with a good, short summary of the problem. So many people are confused between a reference and the expression that names the reference. – Kerrek SB Mar 18 '16 at 22:33

1 Answers1

3

The reason it can't compile is in the error message:

testy.cpp:7:22: error: cannot bind ‘double’ lvalue to ‘double&&’ double&& s() {return l;}

Named variables are always lvalues - l is an lvalue. s() returns an rvalue. You'd have to use a cast to make this work:

double&& s() {return std::move(m); }

Why is that allowed? What scenario will make use of this property?

Is it really worth adding an exception to the language explicitly banning it?

Barry
  • 286,269
  • 29
  • 621
  • 977