为什么在 transforming/copying 向量时进行了这么多复制

why so many copying while transforming/copying vector

为什么有这么多电话复制缺点,我预计只有最后九个?甚至根本不归因于 return 价值优化。

struct C
{
    int _i;
    C(int i) : _i(i) {}
    C(const C& other) { cout << "copy cons from " << other._i << " to " << _i << endl; _i = other._i; }
};
int _tmain(int argc, _TCHAR* argv[])
{
    vector<int> vi{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    vector<C> vc;
    transform(vi.begin(), vi.end(), back_inserter(vc), 
        [](int i) 
    { 
        return C(i); 
    });
}

输出:

copy cons from 1 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 5 to - 842150451
copy cons from 6 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 5 to - 842150451
copy cons from 6 to - 842150451
copy cons from 7 to - 842150451
copy cons from 8 to - 842150451
copy cons from 9 to - 842150451

你的向量 vc 必须增长几次。每次这样做,它都会分配一个更大的内存块,并复制原始元素。

您可以通过使用 std::vector::reserve.

保留足够的 space 来阻止它这样做
vector<C> vc;
vc.reserve(vi.size());

从程序输出可以看出,当一个新元素被添加到向量中时,内存被重新分配,向量中已经存在的元素被复制到新的位置。

您可以在运行避免内存重新分配的算法之前预留足够的内存。

vector<C> vc;
vc.reserve( vi.size() );

在这种情况下,可以避免复制构造函数的重复调用。

但这还不是全部。:)

Class C 有一个转换构造函数

C(int i) : _i(i) {}

通过将使用 lambda 表达式的算法 std::transform 的调用替换为算法 std::copy 而不使用 lambda 表达式,它允许简化向量 vc 的元素的创建。例如

std::copy( vi.begin(), vi.end(), std::back_inserter( vc ) ); 

但这还不是全部。:)

当您使用 std::transformstd::copy 时,会使用两个构造函数:带参数的构造函数和复制构造函数。

您可以避免使用复制构造函数并获得更高效的结果。简单地而不是方法 push_back 最好使用方法 emplace_back

如何使用这个方法?

最简单的就是使用基于范围的for语句

for ( int x : vi ) vc.emplace_back( x );

足够清晰易读。

如果您想使用标准算法,您可以编写

std::for_each( vi.begin(), vi.end(), [&vc]( int x ) { vc.emplace_back( x ); } );

在这两种情况下,只会调用带参数的构造函数,避免调用复制构造函数。

自己检查一下。:)