请考虑以下代码:
#include <vector>
#include <boost/noncopyable.hpp>
struct A : private boost::noncopyable
{
A(int num, const std::string& name)
: num(num),
name(name)
{
}
A(A&& other)
: num(other.num),
name(std::move(other.name))
{
}
int num;
std::string name;
};
std::vector<A> getVec()
{
std::vector<A> vec;
vec.emplace_back(A(3, "foo"));
// vec.emplace_back(3, "foo"); not available yet in VS10?
return vec; // error, copy ctor inaccessible
}
int main ( int argc, char* argv[] )
{
// should call std::vector::vector(std::vector&& other)
std::vector<A> vec = getVec();
return 0;
}
这不能在VS2010下编译,因为显然A
是noncopyable
,因此std::vector<A>
无法复制。因此,我无法从函数返回std::vector<A>
。
然而,考虑到RVO的概念,我觉得这种事情是不可能的。如果此处应用了返回值优化,则可以省略复制构造,并且对getVec()
的调用有效。
那么这样做的正确方法是什么?这在VS2010 / C ++ 11中是否可行?
答案 0 :(得分:11)
如果return vec;
无法编译,则VS2010不支持完全移动语义。通常,如果从函数返回,则自动变量会隐式移动。使用return std::move(vec);
作为临时解决方法,并在脑海中记下您将来摆脱std::move
。
在“退出职能”标题下可以找到in this FAQ answer的完整说明。
此外,您的双参数构造函数会生成由reference-to-const传递的字符串参数的副本。我建议改为以值为基础并将其移入成员:
A(int num, std::string name) : num(num), name(std::move(name)) { }
这样,您可以最大限度地减少必要副本的数量。有关详细信息,请参阅Want Speed? Pass by Value。
此外,由于您的移动构造函数没有做任何特殊操作,您可以default
:
A(A&& other) = default;
这使得它在面对变化时更加强大。错误很少隐藏在你不写的代码中:)
答案 1 :(得分:6)
Elision,像命名返回值优化这样的东西的通用术语,是一种优化。它不是必需。规范允许它,但即使允许,也不会强制执行任何实现。然而,考虑到RVO的概念,我认为这种事情是不可能的。
因此,为了强制允许省略的编译器和不允许省略的编译器之间的一致性,如果操作允许省略,编译器 必须 仍然验证鉴于代码的当前状态,复制/移动被忽略将是可能。因此,如果无法访问复制/移动构造函数,即使编译器实际上不会调用它,操作也会失败。
在这种情况下,Visual Studio 2010似乎在这方面有点混乱。它确实认识到return vec;
应该从vec
移出。但是,似乎VS2010的std::vector
实现需要一个移动赋值运算符来移动;没有一个,它会尝试复制。