std::forward 和带有非常量引用参数的构造函数
在右值引用简介中,完美转发被提出作为将右值 5 转发到具有非常量引用参数的构造函数的理想解决方案。
但是:
#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);
}
在 XCode 4.2 和 G++ 4.6.1 中失败,没有已知的从 int 到 int&
的转换,而:
template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
return std::shared_ptr<T>(new T(/*no forwarding*/a1));
}
编译。我做错了什么?
In A brief Introduction to Rvalue References, perfect forwarding is proposed as the ideal solution to forward rvalue 5 to a constructor with a non-const reference argument.
But:
#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);
}
fails in XCode 4.2 ans G++ 4.6.1 with no known conversion from int to int&
, whereas:
template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
return std::shared_ptr<T>(new T(/*no forwarding*/a1));
}
compiles. What did I get wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您不能将右值绑定到非常量左值引用。本文不建议为此使用完美转发,因为这是不可能的。完美转发将左值转发为左值,将右值转发为右值:
由于示例中的构造函数仅接受左值,因此您只能将左值传递到工厂函数中。传递右值会将其作为右值转发,这将是格式错误的,因为无法将右值传递给该构造函数。
You cannot bind rvalues to non-const lvalue references. The article does not suggest using perfect forwarding for that, because that's not possible. Perfect forwarding forwards lvalues as lvalues, and rvalues as rvalues:
Since the constructor in your example only takes lvalues, you can only pass lvalues into the factory function. Passing an rvalue will forward it as an rvalue, and that will be ill-formed because there's no way to pass an rvalue to that constructor.
我不认为完美转发就是这个意思。这篇文章即使是正确的,也不能暗示这一点,即使是远程的。
相反,它意味着它可以将右值引用作为右值转发,以便调用 move-constructor 或采用 rvaluereferences 的构造函数/函数。
所以你应该尝试这个:
也就是说,从工厂中,应该调用第二个构造函数,而不是你编写的构造函数。
顺便说一句,在这种情况下,构造函数没有多大意义,因为参数类型
int
是基本类型。右值引用作为参数类型用于定义管理资源的类的移动构造函数和移动分配。如果用户定义的类不管理资源,那么移动语义就没有意义。
I don't think perfect forwarding means that. The article, if it is correct, cannot suggest that, even remotely.
Rather, it means it can forward rvalue references as rvalues, so to invoke move-constructor or a constructor/function which takes rvalue references .
So you should try this:
That is, from
factory
, the second constructor should be invoked, not the one which you've written.By the way, in this case, the constructor doesn't makes sense much, because the parameter type
int
is a fundamental type.Rvalue referencs as parameter type is used to defined move-constructor and move-assignment of classes which manages resources. If user-defined class doesn't manage resource, then move-semantics doesn't make sense.
暂时忽略右值引用,假装这是允许的:
您可以看到临时对象被修改,这完全没问题。然而,这种绑定在 C++ 中是非法的,因为它通常是不需要的(毕竟读起来好像 5 被更改为 1)。
所有右值引用所做的都是启用临时值与引用的绑定,但这是安全的,因为我们知道我们正在处理一个应被视为临时的值:
请注意,在此版本的 foo 中,传递到
modify_int
仍然完全没问题。一旦进入函数内部,它是右值引用而不是左值引用这一事实就无关紧要了:我们仍然有一个对象要引用。 在模板中使用转发来保留值类别:没有转发的代码是类似于我答案中的第二个片段。一旦进入工厂函数,a1 只是一个常规左值,并且可以很好地绑定到构造函数引用。但通过转发,它会变回右值(因为
factory(5)
使用右值调用它),它无法绑定到左值引用,从而导致错误。Ignore rvalue-references for a second, and instead pretend this was allowed:
You can see that the temporary object gets modified, which is perfectly fine. However, this binding was made illegal in C++ because its usually not desired (it reads as if 5 was being changed to 1, after all).
All rvalue-references do is enable the binding of temporary values to references, but safely because we understand that we're dealing with a value that should be considered temporary:
Note that in this version of
foo
, passing tomodify_int
is still perfectly fine. Once inside the function, the fact that it was an rvalue-reference instead of an lvalue-reference is irrelevant: we still have an object to refer to. Forwarding is used in templates to preserve the value category:Your code without forwarding is similar to the second snippet in my answer. Once inside the
factory
function,a1
is just a regular lvalue, and binds to the constructor reference just fine. But with forwarding, it turns back into an rvalue (becausefactory(5)
calls it with an rvalue), which cannot bind to the lvalue-reference, resulting in an error.