【问题标题】:Need Meyers Effective C++ Widget rvalue example explanationNeed Meyers Effective C++ Widget rvalue 实例说明
【发布时间】:2014-11-17 10:18:07
【问题描述】:

我有一点 C++ 问题。

在 Effective Modern C++ 的第一页,有一个例子:

class Widget {
public:
    Widget(Widget&& rhs);
};

另外,还有一条评论:'rhs 是一个左值,尽管它有一个右值引用类型'。

说实话,我什么都不懂。 'rhs 是一个左值,但它的类型是右值引用'是什么意思?

【问题讨论】:

  • 嗯,谢谢各位的解释,我终于明白了!每个人都很荣幸。

标签: c++ c++11 rvalue-reference lvalue


【解决方案1】:

请记住,这里有两个不同的东西:

  • 一个与变量的类型有关:有两种类型的引用:左值引用(&)和右值引用(&&)。

    这决定了函数优先接受什么并且总是“显而易见的”,因为你可以从类型签名中读取它(或使用decltype)。

  • 另一个是表达式的属性(或值):表达式可以是左值或右值 (actually, it's more complicated than that...)。

    这个属性没有直接体现在代码中(但有一个经验法则,见下文),但你可以在重载决议中看到它的效果。特别是,

    • 左值参数更喜欢绑定到左值引用参数,而
    • 右值参数更喜欢绑定到右值引用参数。

这些属性密切相关(在某种意义上是“双重”的),但它们不一定彼此一致。尤其要认识到变量表达式实际上是不同的东西,所以从形式上讲,它们甚至没有可比性,“苹果对橙子”。


在 C++ 中有一条规则,即使您已将 rhs 声明为右值引用(意味着它将优先匹配作为右值的 参数),在移动构造函数块中,变量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 所指出的,这在技术上并不正确——请参阅他的评论以获得更正式的描述。)

【讨论】:

  • 然后你使用std::move() 去掉它的名字(把它变成一个右值)。
  • “即使您已将 rhs 声明为右值引用 [...],变量 rhs 本身仍将表现为左值”在标准语言中,有人会说即使您声明了一个名为rhs 的变量/参数,并且类型为“对Widget 的右值引用”,引用该变量的表达式也是一个左值表达式。我>
  • 类似地:“如果表达式有名字” 表达式没有名字。表达式可以包含名称/“名称”(作为表达式的标识符,即 id-expressions)可以是表达式。标识符表达式是左值,除非它引用枚举数。 (但我在吹毛求疵,+1)
  • 我有点引用别人所说的话,但是将表达式和变量等同起来在技术上是不正确的。不过,我不想用太多的标准语来阻碍答案:)
【解决方案2】:

简短的解释:P

Widget(Widget&& rhs);

是一个移动构造函数。它将接受一个右值作为参数。在移动构造函数的定义中,您可以使用名称 rhs 来引用另一个 Widget,因此它是一个左值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 2015-05-03
    • 1970-01-01
    • 2013-07-16
    相关资源
    最近更新 更多