我正在为linux和&编写一个应用程序Windows,并注意到GCC构建产生了很多对拷贝构造函数无用的调用。
以下是产生此行为的示例代码:
struct A
{
A() { std::cout << "default" << std::endl; }
A(A&& rvalue) { std::cout << "move" << std::endl; }
A(const A& lvalue) { std::cout << "copy" << std::endl; }
A& operator =(A a) { std::cout << "assign" << std::endl; return *this; }
};
BOOST_AUTO_TEST_CASE(test_copy_semantics)
{
std::vector<A> vec_a( 3 );
}
此测试仅创建3个元素的向量。我希望有3个默认构造函数调用和0个副本,因为没有A
左值。
在Visual C ++ 2010中,输出为:
default
move
default
move
default
move
在GCC 4.4.0(MinGW)中,( - 02 -std = c ++ 0x),输出为:
default
copy
copy
copy
发生了什么,我该如何解决?副本对于实际的类来说是昂贵的,默认构造和移动都很便宜。
答案 0 :(得分:18)
两种实现(Visual C ++ 2010和GCC 4.4.0)都有错误。正确的输出是:
default
default
default
这在23.3.5.1 [vector.cons] / 4中指定:
要求:T应为DefaultConstructible。
不允许实现假设A是MoveConstructible或CopyConstructible。
答案 1 :(得分:6)
看起来问题是你拥有的g ++版本没有C ++ 0x完全兼容的库。特别是,在C ++ 03中,std :: vector的size构造函数具有以下签名:
// C++ 03
explicit vector(size_type n, const T& value = T(),
const Allocator& = Allocator());
使用该函数签名和您的调用,创建临时,然后由常量引用绑定,并为每个元素创建它的副本。
在C ++ 0x中有不同的构造函数:
// C++0x
explicit vector(size_type n);
vector(size_type n, const T& value, const Allocator& = Allocator());
在这种情况下,你的调用将与第一个签名匹配,并且元素应该默认构造,并在容器上放置new(正如@Howard Hinnant在他的answer中正确指出的那样,编译器不应该调用此移动构造函数。)
您可以尝试检查更新版本的g ++是否有更新的标准库,或者您可以通过手动添加元素来解决此问题:
std::vector<A> v;
v.reserve( 3 ); // avoid multiple relocations
while (v.size() < 3 ) v.push_back( A() );
答案 2 :(得分:1)
然后试试这个:
std::vector<A> vec_a;
vec_a.reserve(3);
for (size_t i = 0; i < 3; ++i)
vec_a.push_back(A());
您要做的是强制初始化过程为每个值而不是构造使用构造+移动,然后复制/复制/复制。这些只是不同的哲学;图书馆的作者不可能知道哪种类型对任何特定类型都是最好的。
答案 3 :(得分:1)
在将默认构造对象复制到“this”对象时,可以添加特殊(便宜)案例来复制ctor算法。这只是一种解决方法,然而,这种行为很奇怪。 两个编译器(库)在堆栈上创建一个临时对象,然后gcc将此临时副本复制到目标3次; msvc重新创建临时对象3次(!)(在堆栈上)并移动3次到目标。我不明白他们为什么不直接创建对象。
答案 4 :(得分:0)
我认为,所有3种变体都不违反C ++ 0x草案。它需要以下: 1.构造具有n个值初始化元素的向量 2.T应为DefaultConstructible 3.线性n
所有3种变体都满足1,默认+复制,默认+移动等同于默认值 所有3种变体满足3 所有3种变体都满足2:它们适用于DefaultConstructible类型。特定算法可用于Moveable类型。 STL中的一般做法是对具有不同功能的类型使用不同版本的算法。