【问题标题】:rvalue reference matching (perfect forwarding example)右值引用匹配(完美转发示例)
【发布时间】:2016-01-01 07:50:57
【问题描述】:

我对以下完美的转发功能感到困惑,其中模板参数T可以匹配右值或左值引用:

template<typename T>
void foo(T&& t){
    T::A; // intended error to inspect type
}

int main(){
    std::vector<int> a;
    std::vector<int> && b = std::move(a);

    foo(b);             // T is std::vector<int> & 
    foo(std::move(a));  // T is std::vector<int>
}

我不明白为什么这两种情况下fooT的模板参数推导如此不同? t在函数foo中的类型是什么,最重要的区别是什么。

std::move(a) 返回一个右值引用,而b 已经是一个右值引用(但有一个名称)。

对吗,b 的类型是对std::vector&lt;int&gt; 的右值引用,但据我了解,它有一个名称,因此在函数 main 中被视为左值?

任何人都可以对此有所了解吗:-)

【问题讨论】:

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


【解决方案1】:

表达式ab 都是左值,而表达式std::move(a) 是右值。

参数T 的推导利用了特殊的引用折叠规则,以便t 的类型是左值或右值引用,根据需要绑定到函数调用参数。

【讨论】:

    【解决方案2】:

    对吗,b 的类型是对std::vector&lt;int&gt; 的右值引用,但据我了解,它有一个名称,因此在函数 main 中被视为左值?

    是的,就是这样。如果您考虑右值引用函数参数,则更有意义:调用者指定该函数可以对其获取的对象执行任何操作。所以从函数体内部,为了确保代码真的可以用它做任何事情,参数应该被视为左值。同样的论点也可以用于其他右值引用,包括您示例中的 b,尽管程度较轻。

    【讨论】:

      【解决方案3】:

      如果您考虑函数的签名,则参数的类型是T&amp;&amp;。在您的第二个示例中,T 被推导出为vector&lt;int&gt;,这意味着您的函数的参数类型是vector&lt;int&gt;&amp;&amp;。所以你仍然是通过(右值)引用。

      在另一种情况下,您将T 推断为vector&lt;int&gt;&amp;。所以参数的类型是vector&lt;int&gt; &amp; &amp;&amp;... 或者是,但不允许引用引用。引用折叠接管,任何涉及左值引用的双重引用都成为左值引用。所以你是通过左值引用传递的。

      就 b 而言,这是一个众所周知的右值引用问题。本质上,b 的类型是右值引用,但是 b 本身仍然有一个左值的值类别。可以这样想:b 本身就是一个变量,它必须存在于堆栈的某个地方,并且有一个地址。所以它是一个左值。这正是需要转发参数时调用 std::forward 的方式。如果您不这样做,那么它们将始终作为左值参数转发。

      我真的推荐这篇 Scott Meyers 文章:https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers。请仔细阅读!

      【讨论】:

        【解决方案4】:

        && 与模板一起使用时,有一个特殊的类型推导规则。

        template <class T>
        void func(T&& t) {
        }
        

        "当 && 出现在类型推断上下文中时,T&& 获得一个特殊的 意义。当func被实例化时,T取决于参数是否 传递给 func 是左值或右值。如果它是 U 类型的左值, T 推导出为 U&。如果是右值,则推导 T 为 U:"

        func(4);            // 4 is an rvalue: T deduced to int
        
        double d = 3.14;
        func(d);            // d is an lvalue; T deduced to double&
        
        float f() {...}
        func(f());          // f() is an rvalue; T deduced to float
        
        int bar(int i) {
          func(i);          // i is an lvalue; T deduced to int&
        }
        

        另外,参考折叠规则是一个很好的阅读。

        查看这个以获得非常好的解释:

        perfect forwarding

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-11-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-02-09
          • 1970-01-01
          相关资源
          最近更新 更多