std :: forward和带有非const引用参数的构造函数

时间:2011-12-02 11:02:04

标签: c++ c++11 perfect-forwarding

Rvalue References简介中,提出了完美转发作为将rvalue 5转发到具有非const引用参数的构造函数的理想解决方案。

可是:

#include <memory>
#include <iostream>
#include <utility>

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
   return std::shared_ptr<T>(new T(std::forward<A1>(a1)));
}

class X {
public:
    X(int& i){
        std::cout<<"X("<<i<<")\n";
    }
};


int main() {
    std::shared_ptr<X> p = factory<X>(5);
}

在使用no known conversion from int to int&的XCode 4.2和G ++ 4.6.1中失败,而:

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
   return std::shared_ptr<T>(new T(/*no forwarding*/a1));
}

编译。我出了什么问题?

3 个答案:

答案 0 :(得分:5)

  

完美转发被认为是将rvalue 5转发到具有非const引用参数的构造函数的理想解决方案。

我认为完美的转发意味着。如果这篇文章是正确的,这篇文章甚至不能暗示这一点。

相反,它意味着它可以将rvalue引用转发为rvalues,因此要调用move-constructor或带有rvalue引用的构造函数/函数。

所以你应该试试这个:

class X {
public:
    X(int& i){
        std::cout<<"X("<<i<<")\n";
    }

    //this is a constructor which takes rvalue references
    X(int&& i){ 
        std::cout<<"X("<<i<<")\n";
    }
};

也就是说,从factory开始,应该调用第二个构造函数,而不是你编写的构造函数。

顺便说一下,在这种情况下,构造函数没有多大意义,因为参数类型int是基本类型。

Rvalue referencs作为参数类型用于定义管理资源的类的移动构造函数和移动分配。如果用户定义的类不管理资源,则移动语义没有意义。

答案 1 :(得分:5)

您无法将rvalues绑定到非const左值引用。该文章并未建议使用完美转发,因为这是不可能的。完美转发左值作为左值,左值作为右值:

  

这里,forward保留了参数的左值/右值   被传递到工厂。如果rvalue传递给工厂,那么   rvalue将在前锋的帮助下传递给T的构造函数   功能。同样,如果将左值传递给工厂,它就是   作为左值转发给T的构造函数。

由于示例中的构造函数只接受左值,因此只能将左值传递给工厂函数。传递一个右值会将它作为一个右值转发,因为没有办法将一个右值传递给该构造函数,所以它会形成错误。

答案 2 :(得分:3)

忽略rvalue-references一秒钟,而是假装允许这样做:

void modify_int(int& i)
{
    i = 1;
}

void foo(int& x)
{
    modify_int(x); // okay, modify_int references x
}

int i = 7;
foo(i); // makes i = 1

// illegal in standard C++, cannot bind a temporary to a non-const reference
foo(5); // makes the temporary integer equal to 1

您可以看到临时对象被修改,这非常好。但是,这种绑定在C ++中是非法的,因为它通常不是所希望的(毕竟它看起来好像5被改为1)。

所有rvalue-references都支持将临时值绑定到引用,但是安全,因为我们知道我们正在处理应该被视为临时值的值:

void modify_int(int& i)
{
    i = 1;
}

void foo(int&& x)
{
    modify_int(x); // okay, modify_int references x
}

int i = 7;
foo(std::move(i)); // makes i = 1 (std::move makes it an rvalue)

// legal in C++11, temporary is bound to rvalue-reference
foo(5); // makes the temporary integer equal to 1

请注意,在此版本的foo中,传递给modify_int仍然完全没问题。一旦进入函数,它是一个rvalue-reference而不是lvalue-reference的事实是无关紧要的:我们仍然有一个对象可以引用。 Forwarding is used in templates to preserve the value category

void test(int& i) {} // lvalue version of test
void test(int&& i) {} // rvalue version of test

template <typename T>
void foo(T&& x)
{
    // if x was an lvalue, forward does nothing;
    // if x was an rvalue, forward std::move's it 
    test(std::forward<T>(x)); 
}

int i = 7;
foo(i); // calls lvalue version of test

foo(5); // calls rvalue version of test

您的代码没有转发类似于我的答案中的第二个代码段。进入factory函数后,a1只是一个常规左值,并且很好地绑定到构造函数引用。但是通过转发,它会转回rvalue(因为factory(5)使用rvalue调用它),它无法绑定到左值引用,从而导致错误。