2

以下示例代码编译。

#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;
}

我的编译器命令:

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

如果我们注释掉第 1 行,g++ 会抱怨:

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)]

当然,错误很明显,C++03 中的一个典型错误:References from rvalues are not allowed, if these rvalues are not const. 但为什么它与右值构造函数(即#ifdef启用)一起工作?我们在初始化列表中有同样的情况:从非常量值引用。

另外,如果你解释一下……这段代码是“良好的编码风格”还是“避免”?

4

2 回答 2

6

答案很简单:任何有名字的东西 - 它都是一个左值。因此,在您的 ctor 中,具有右值引用,_data是 ctor 主体中的左值,尽管事实上它是外部世界中的右值。所以最后你有一个悬空的参考,所以你不应该做这样的事情。

基本上,当您接受某些东西时&&(除非在模板中,即通用引用),您应该将其视为“此值将被销毁”。所以你应该从它那里窃取,改变它,无论如何(除了让它处于某种无效状态以防止其正常破坏)。但是你永远不应该存储指针或对它的引用。因为这个对象可能会在你的函数完成后立即销毁。这正是你的情况。double(1.0f) 仅存在于您的 ctor 主体中,并且在它之外没有任何意义,您仍然存储对它的引用。

于 2013-11-11T09:20:58.473 回答
3

这是因为命名的 r 值不是 r 值。

看这个:

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

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

分配ref2ref3是可以的,因为ref2它是一个命名的 r 值。

同样,在您的 ctor_data中不是 r 值,而是 l 值。

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

当将参数声明为采用 r 值时,您需要将 r 值传递给函数,但参数本身成为命名引用,因此被转换为 l 值。如果你想将它作为 r 值传递给另一个函数,你必须使用std::move它。

于 2013-11-11T09:20:49.320 回答