【问题标题】:Rvalue (Reference) in C++11C++11 中的右值(参考)
【发布时间】:2014-10-19 00:20:17
【问题描述】:

关于右值的话题很多(参考)。

但我还没有找到以下问题的答案:

根据http://en.cppreference.com/w/cpp/utility/move,std::move() 将右值引用作为参数。

根据 C++11 标准 http://i.stack.imgur.com/YKlod.png 中的图表,左值不会是右值。

std::move()的常用用法:

Foo f;
std::deque<Foo> fq;
fq.push_back(std::move(f));

f 是左值,因为它是符合 C++11 标准的对象:

左值(所谓的,历史上,因为左值可能出现在赋值表达式的左侧)指定一个函数或一个对象。 [示例:如果 E 是指针类型的表达式,则 *E 是一个左值表达式,指的是 E 指向的对象或函数。再举一个例子,调用返回类型为左值引用的函数的结果是左值。]

然后,理论上,std::move(f) 应该给我一个编译错误,但它没有。我确定我错过了什么......

欢迎任何意见。

【问题讨论】:

  • 实际上,用 std::move 大惊小怪是多余的。 std::vector 有 emplace_back 用于此目的
  • 实际上,std::move 不采用右值引用,而是采用转发引用(又名通用引用),这意味着它可以是左值和右值引用。
  • @remyabel 什么是“折叠规则”?
  • @Deduplicator 所以 T&& 是转发参考/通用参考?那么,我们如何将右值引用指定为参数的一种类型呢?
  • @Hei 只是在推导T的上下文中,如std::move-template的原型。在这种情况下,如果您真的想限制为右值引用,请使用 SFINAE 或 static_assert 和类型特征。

标签: c++11


【解决方案1】:

std::move 的参数是T &amp;&amp; 类型,其中推导出T。这称为转发引用。*

诀窍是模板生成的声明T &amp; &amp;&amp; 等价于T &amp;。当你将一个左值int 传递给move 时,则T &amp;&amp; 中的T 类型推导出T = int &amp;,整个参数为int &amp; &amp;&amp; = int &amp; 类型。这称为引用折叠

如您所见,将左值传递给右值参数是非法的,但引用折叠可以防止这种情况发生。

通过声明这样的引用来定义与左值或右值引用一起使用的函数的做法称为完美转发,因此该引用是一个转发引用。参数是“转发”的,因为它在函数内部的行为与在函数调用中的行为相同,前提是 std::forward 用于恢复右值。

* 术语通用参考deprecated

【讨论】:

    【解决方案2】:

    正如@Deduplicator 所述,&amp;&amp; 仅在T 是推导类型的情况下表示通用引用。 Scott Meyer 的comprehensive blog post on Universal References 提供了一个易于记忆的引语:

    如果变量或参数被声明为 T&& 类型 推导出类型 T,该变量或参数是一个通用引用。

    以及代码示例:

    Widget&& var1 = someWidget; // here, “&&” means rvalue reference
    
    auto&& var2 = var1; // here, “&&” does not mean rvalue reference
    
    template<typename T>
    void f(std::vector<T>&& param); // here, “&&” means rvalue reference
    
    template<typename T>
    void f(T&& param);               // here, “&&”does not mean rvalue reference
    

    正如Thomas Becker 所述,std::move 接受通用引用以利用完美转发。

    尽管 push_back 采用 T&amp;&amp;,但这不是通用引用,正如 Scott Meyers 所解释的那样。 T 在这个例子中已经被推导出来了:

    template <class T>
    void vector<T>::push_back(T&& x);
    

    所以参数实际上变成了一个右值引用。举一个实际的例子,考虑一个没有采用左值引用的重载的模板类:

    template <typename T>
    struct Test
    {
        void push_back(T&& t);
    };
    
    template <typename T>
    void Test<T>::push_back(T&& t) { std::cout << "rvalue overload.\n"; }
    
    int main()
    {
        Test<int> t;
        t.push_back(42);
        int i = 50;
        t.push_back(i);
        return 0;
    }
    

    t.push_back(i) 会报错。

    【讨论】:

      【解决方案3】:

      “右值引用”并不意味着是右值的引用。它只是类型的描述(T&amp;&amp; 而不是T&amp;)。在这方面它的名称令人困惑。 f 的值类别确实是lvalue,没关系。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-11
        • 2013-07-12
        • 1970-01-01
        相关资源
        最近更新 更多