标准库容器在GCC中的rvalues上生成大量副本

Inv*_*rse 22 c++ gcc mingw rvalue-reference c++11

我正在为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 );
}
Run Code Online (Sandbox Code Playgroud)

此测试仅创建3个元素的向量.我期望3个默认构造函数调用和0个副本,因为没有A左值.

在Visual C++ 2010中,输出为:

default
move
default
move
default
move
Run Code Online (Sandbox Code Playgroud)

在GCC 4.4.0(MinGW)中,( - 02 -std = c ++ 0x),输出为:

default
copy
copy
copy
Run Code Online (Sandbox Code Playgroud)

发生了什么,我该如何解决?副本对于实际类来说是昂贵的,默认构造和移动都很便宜.

How*_*ant 18

这两种实现(Visual C++ 2010和GCC 4.4.0)都有错误.正确的输出是:

default
default
default
Run Code Online (Sandbox Code Playgroud)

这在23.3.5.1 [vector.cons]/4中规定:

要求:T应为DefaultConstructible.

不允许实现假设A是MoveConstructible或CopyConstructible.

  • 我已经报告了Visual C++问题. (4认同)

Dav*_*eas 6

看起来问题是你拥有的g ++版本没有C++ 0x完全兼容的库.特别是,在C++ 03中,std :: vector的size构造函数具有以下签名:

// C++ 03
explicit vector(size_type n, const T& value = T(),
const Allocator& = Allocator());
Run Code Online (Sandbox Code Playgroud)

使用该函数签名和您的调用,创建临时,然后由常量引用绑定,并为每个元素创建它的副本.

而在C++ 0x中有不同的构造函数:

// C++0x
explicit vector(size_type n);
vector(size_type n, const T& value, const Allocator& = Allocator());
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您的调用将匹配第一个签名,并且元素应该默认构造,并在容器上放置new(正如@Howard Hinnant在他的回答中正确指出的,编译器根本不应该调用移动构造函数).

您可以尝试检查更新版本的g ++是否有更新的标准库,或者您可以通过手动添加元素来解决此问题:

std::vector<A> v;
v.reserve( 3 );     // avoid multiple relocations
while (v.size() < 3 ) v.push_back( A() );
Run Code Online (Sandbox Code Playgroud)