了解完美的转发

时间:2019-02-21 19:59:37

标签: c++ move-semantics

据我了解,作为参数传递给函数的右值变成左值, 如果参数作为rvalue传递,则std :: forward返回rvalue,如果参数作为lvalue传递则返回lvalue。这是我的课程:

#include <string>
#include <iostream>

struct MyClass
{
    MyClass()
    {
        std::cout << "default";
    }

    MyClass(const MyClass& copy)
    {
        std::cout << "copy";
    }

    MyClass& operator= (const MyClass& right)
    {
        std::cout << "=";
        return *this;
    }

    MyClass& operator= (const MyClass&& right)
    {
        std::cout << "mov =";
        return *this;
    }

    MyClass(MyClass&& mov)
    {
        std::cout << "mov constructor";
    }
};

void foo(MyClass s)
{
    MyClass z = MyClass(std::forward<MyClass>(s));
}

void main()
{
    auto a = MyClass();
    foo(MyClass()); //z is created by move_constructor
    foo(a); //z is created by move_constructor, but I think it must be created using copy constructor
}

我的问题是:为什么在两种情况下都使用move_constructor创建z变量。 我认为它必须在第一种情况foo(MyClass())中移动,并在第二种情况下foo(a)中复制。在第二种情况下,我将左值作为参数s传递,并且std :: forward必须返回左值,然后将其作为左值引用传递到MyClass构造函数中。我在哪里错了?

2 个答案:

答案 0 :(得分:1)

我认为你很困惑。当通用引用开始起作用时,转发的角色很重要,而通用引用就像T&& t一样,但是当T是模板参数时。 >

例如,在void foo(X&& x);中,x不是转发引用,它是普通的右值引用,转发是没有意义的。相反,如果要保留其右值,请使用std::move,否则它将变为左值:

void foo(X&& x) {
     bar(x); // calls bar with an l-value x, x should be not moved from

     baz(std::move(x)); // calls bar with an r-value x, x is likely moved from after this and probably unusable
}

换句话说,上面的函数foo经过专门设计,以右值引用作为参数,并且不接受其他任何内容。作为函数编写者,您以这种方式定义了合同。

相反,在template <class T> void foo(T&& t)这样的上下文中,t是转发引用。由于引用折叠规则,它可能是右值引用或左值引用,具体取决于在调用位置提供给函数foo的表达式的值。在这种情况下,您使用

template<class T>
void foo(T&& t) {
    // bar is called with value matching the one at the call site                  
    bar(std::forward<T>(t));
}

答案 1 :(得分:0)

您声明的参数类型为MyClass。无论哪种初始化参数的表达式在您的函数中都是无关紧要的,它不会影响参数的类型。

MyClass不是引用类型。 std::forward将非引用类型的左值表达式转换为右值。在这种情况下,std::forward的使用等同于std::move

请注意,参数本身是在调用foo(a)中复制构造的。