在 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));
}
编译。我出了什么问题?
答案 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调用它),它无法绑定到左值引用,从而导致错误。