以下示例代码编译。
#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值的引用。
另外,如果你解释一下......这段代码是“良好的编码风格”还是“避免”?
答案 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
它。