为什么通过rvalue初始化非const引用(在C ++ 11中)?

时间:2013-11-11 09:09:56

标签: c++ c++11 rvalue

以下示例代码编译。

#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中的典型错误:如果这些rvalues不是const,则不允许来自rvalues的引用。但为什么它适用于rvalue构造函数(即启用#ifdef)?我们在初始化列表中具有相同的情况:来自非const值的引用。

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

2 个答案:

答案 0 :(得分:6)

答案很简单:无论名称是什么 - 它都是左值。所以在你的ctor中,使用rvalue引用,_data是ctor体中的左值,尽管它是外部世界的右值。所以最后你有一个悬空参考,所以你不应该做这样的事情。

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

答案 1 :(得分: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.

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

同样地,你的ctor _data不是一个r值,而是一个l值。

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

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