我有一个小小的C ++问题。
在Effective Modern C ++的第一页上,有一个例子:
class Widget {
public:
Widget(Widget&& rhs);
};
此外,还有一条评论:' rhs是一个左值,虽然它有一个左值引用类型'。
说实话,我什么都不懂。它是什么意思' rhs是左值,但它的类型是右值参考'?
答案 0 :(得分:7)
请记住,这里有两个截然不同的事情:
一个与变量的类型有关:有两种类型的引用:左值引用(&
)和右值引用(&&
)。< / p>
这决定了该功能优先接受的功能,并且始终是&#34;显而易见的&#34;因为你可以从类型签名中读取它(或使用decltype
)。
另一个是表达式(或值)的属性:表达式可以是左值或右值(actually, it's more complicated than that...)。
此属性不直接在代码中显示(但有一条经验法则,见下文),但您可以在重载决策中看到它的效果。特别是,
这些属性密切相关(在某种意义上说,彼此之间是双重的),但它们并不一定是彼此一致的。特别重要的是,认识到变量和表达式实际上是不同的东西,所以正式地说它们甚至不具有可比性,&#34;苹果到橘子&#34;。
在C ++中有这样的规则,即使你已经声明rhs
是一个右值引用(意味着它优先匹配参数是rvalues),移动构造函数,变量rhs
本身仍将表现为左值,因此优先匹配接受左值引用的函数。
void test(Widget&&) { std::cout << "test(Widget&&): called\n"; }
void test(Widget&) { std::cout << "test(Widget&): called\n"; }
Widget::Widget(Widget&& rhs) {
// here, `rhs` is an lvalue, not an rvalue even though
// its type is declared to be an rvalue reference
// for example, this will match `test(Widget&)`
// rather than the `test(Widget&&)` overload, which may be
// a bit counter-intuitive
test(rhs);
// if you really want to match `test(Widget&&)`
// you must use `std::move` to "wrap" the variable
// so that it can be treated as an rvalue
test(std::move(rhs));
}
这样做的理由是防止移动构造函数内的意外移动。
一般的经验法则是:如果表达式有一个名称(即由一个命名变量组成),那么它就是一个左值。如果表达式是匿名的,那么它就是一个右值。 (正如dyp所说,这在技术上并不正确 - 请参阅他的评论以获得更正式的描述。)
答案 1 :(得分:2)
简短的解释:P
Widget(Widget&& rhs);
是一个移动构造函数。它将接受右值作为参数。在移动构造函数的定义中,您可以使用名称rhs引用另一个Widget,因此它是一个左值。