【发布时间】:2015-11-18 12:03:38
【问题描述】:
尝试运行这个测试程序:
#include <iostream>
class A {
public:
A() { std::cout << "empty constructor" << std::endl; }
A(const A &a) { std::cout << "copy constructor" << std::endl; }
A(A &&a) { std::cout << "move constructor" << std::endl; }
~A() { std::cout << "destructor" << std::endl; }
A &operator=(const A &o) { std::cout << "copy assignment" << std::endl; return *this; }
A &operator=(A &&o) { std::cout << "move assignment" << std::endl; return *this; }
};
A outside;
template <typename U>
void bar(U &&a) {
outside = std::forward<U>(a);
}
void foo(A &&a) {
bar(a);
}
int main(int argc, char *argv[]) {
foo(A());
return 0;
}
在我的编译器上,我认为是 GCC 4.4.1,输出是:
empty constructor
empty constructor
copy assignment // what??
destructor
destructor
在我看来,第三行很奇怪。为什么outside 在bar 中复制a 而不是移动它?换句话说,为什么foo 使用bar(A &a) 实例化bar 而不是bar(A &&a)?
我应该提到,通过将foo 更改为
template <typename U>
void foo(U &&a) {
bar(std::forward<U>(a));
}
因此使用移动赋值运算符。
在我的代码中,我有很多委托方法,我不想将它们全部放在一个 .h 文件中并为它们制作模板。是否有另一种方式告诉编译器或使其了解a 在foo 中用作右值?
编辑:一些人指出了这一变化:
void foo(U &&a) {
bar(std::move(a));
}
我太慢了,无法表明我希望foo 能够接受两者:
int main(int argc, char *argv[]) {
foo(A());
return 0;
}
和
int main(int argc, char *argv[]) {
A a;
foo(a);
return 0;
}
在第二种情况下,建议的代码将错误地窃取a 在main 中的资源,应将其用作参考。
【问题讨论】:
-
因为
ainfoo是一个左值。你需要bar(std::move(a));。我正在寻找一个好的副本。 -
我已编辑问题以更清楚地解释问题。谢谢!
-
@bombax:真的没有别的办法。如果您想要一个转发的方法,那么您需要复制该方法或对其进行模板化。
-
如果还不清楚:即使你写了
foo( (A&&)a )(这是你的g++版本正在做的),那么foo没有关于它是否被调用的信息临时对象与否;您必须复制 (bar(a)) 或窃取资源 (bar(std::move(a)))。仅从右值(不使用模板)窃取的正确方法是有一对重载void foo(A& a) { bar(a); } void foo(A&& a) { bar(std::move(a)); } -
重载对的替代方案是
template<typename T> void foo(T &&a) { bar(std::forward(a)); },因为转发引用可以推导出引用类型;但我猜你没有这样做,因为你本来可以打电话给bar:)