Question

The following example code compiles.

#define USE_RVALUE // line 1

template<class data_type>
class Container
{
    data_type& data;
public:
#ifdef USE_RVALUE
    Container(data_type&& _data) : data(_data) {}
#endif
    Container(data_type& _data) : data(_data) {}
};

int main()
{
    double d = 42.0;
    Container<double> c1(d);
    Container<double> c2(1.0f); // line 18
    return 0;
}

My compiler command:

g++ -std=c++11 -Wall ref.cpp -o ref # g++ is 4.7.1

If we outcomment line 1, g++ complains:

no matching function for call to ‘Container<double>::Container(float)’
ref.cpp:18:34: note: candidates are:
ref.cpp:11:9: note: Container<data_type>::Container(data_type&) [with data_type = double]
ref.cpp:11:9: note:   no known conversion for argument 1 from ‘float’ to ‘double&’
[... (more candidates)]

Of course, the error is clear, and a typical error in C++03: References from rvalues are not allowed, if these rvalues are not const. But why does it work with the rvalue constructor (i.e. the #ifdef enabled)? We have the same situation in the initializer list: Reference from non-const value.

Also, if you explain it... Is this code "good coding style" or "to avoid"?

Was it helpful?

Solution

The answer is simple enough: whatever has name - it is an lvalue. So in your ctor, with rvalue reference, _data is an lvalue in the body of ctor despite the fact it is an rvalue in the outer world. So at the end you have a dangling reference so you should not do such a thing.

Basically, when you accept something by &&(except when in template, aka Universal references) you should treat it as "this value are going to be destroyed". So you should steal from it, change it, whatever(except leaving it in some invalid state preventing its normal destruction). But you should never store pointer or reference to it. Because this object maybe destroyed right after your function complete. It is exactly the case you have. double(1.0f) exists in your ctor body only and has no meaning outside it and you still store reference to it.

OTHER TIPS

It's because named r-values are not r-values.

Look at this:

double& ref = 5;        // Error
const double& cref = 5; // Ok

double&& ref2 = 5;      // Ok
double& ref3 = ref2;    // Ok. ref2 is not r-value.

Assigning ref2 to ref3 is ok because ref2 is a named r-value.

In the same way, in your ctor _data is not an r-value, but an l-value.

Container(data_type&& _data) : data(_data) {}

When declaring parameters as taking an r-value you make it a requirement to pass an r-value to the function, but the argument itself becomes a named reference and is thus converted into an l-value. If you want to pass it to another function as an r-value you have to std::move it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top