我想它是有效的,根据您的定义。
测试不同的版本;
Copies: 100000005 , Construct: 1, Equal copies 0
real 0m0.075s
user 0m0.073s
sys 0m0.002s
和 emplace_back:
Copies: 0 , Construct: 100000005, Equal copies 0
real 0m0.195s
user 0m0.191s
sys 0m0.004s
您可能是说它节省空间。但是,这是基于用例的选择,而且编译器设计者似乎更喜欢速度。
这里是代码(我跟踪也一样)
struct Counter
{
static int count_ctor;
static int count_copy;
static int count_equal;
Counter(int){count_ctor++;}
Counter(const Counter&){count_copy++;}
Counter(Counter&&) noexcept{}
Counter & operator=(Counter const &){ count_equal++ ;}
};
int Counter::count_copy = 0;
int Counter::count_ctor = 0;
int Counter::count_equal = 0;
int main(void)
{
int size(100000005);
#ifdef EMPLACE
std::vector<Counter> v;
v.reserve(size);
for(int i = size; i>0 ; --i){ v.emplace_back(0);}
#else
std::vector<Counter> v(size,0);
#endif
std::printf("Copies: %d , Construct: %d, Equal copies %d",Counter::count_copy, Counter::count_ctor, Counter::count_equal);
return 0;
}
使用 g++ -DEMPLACE --std=c++11 -O3 或不使用 EMPLACE 编译以获得所需的二进制文件。
第二次测试
为了反驳 OP 的假设,做了以下测试:
- 在多个较大的类中创建许多小向量
- 使用默认构造复制策略或通过调用 emplace 包装函数创建的所有对象。
我们用
生成了两个二进制文件
g++ -DEMPLACE --std=c++11 -O3 copyc.cpp -o copyc && g++ --std=c++11 -O3 copyc.cpp -o copyc_copy
为了避免两者中的任何一个受到操作系统的优待,我们在它们之间设置了 10 秒的标准暂停,并在系统空闲时启动。
下面是一个示例性的运行。
export K=10192 ; time ./copyc_copy $K ; sleep 10; time ./copyc $K
Copies: 10192 , Construct: 1, Equal copies 0
real 0m2.888s
user 0m0.666s
sys 0m2.219s
Copies: 0 , Construct: 10192, Equal copies 0
real 0m3.376s
user 0m1.105s
sys 0m2.270s
我在多种情况下都运行过这个,也反过来
Copies: 0 , Construct: 10192, Equal copies 0
real 0m3.154s
user 0m0.886s
sys 0m2.267s
Copies: 10192 , Construct: 1, Equal copies 0
real 0m2.573s
user 0m0.531s
sys 0m2.025s
话虽如此,这是一个不完整的测试,但是在这个匹配时间上,我敢打赌编译器设计者做的更多,从 gnu 到 clang 和 VS 都决定实现一个构造复制策略。我敢肯定他们还有其他原因。
第二次测试的代码如下:
#include <vector>
#include <iostream>
#include <cstdlib>
template<typename T> static std::vector<T> get5()
{
std::vector<T> s;
s.reserve(5);
for(int i=5; i!=0 ;--i)
{
s.emplace_back(T());
}
return s;
}
struct test_struct
{
volatile int internals[255];
};
struct test_create
{
std::vector<test_struct> s;
test_create() : s(5){}
};
struct test_emplace
{
std::vector<test_struct> s;
test_emplace() : s(get5<test_struct>()){}
};
struct Counter
{
static int count_ctor;
static int count_copy;
static int count_equal;
#ifdef EMPLACE
test_emplace t[100];
#else
test_create t[100];
#endif
Counter(int)
{
count_ctor++;
}
Counter(const Counter&)
{
count_copy++;
}
Counter(Counter&&) noexcept
{
}
Counter & operator=(Counter const &){ count_equal++ ;}
};
int Counter::count_copy = 0;
int Counter::count_ctor = 0;
int Counter::count_equal = 0;
int main(int arg, char const * argv[])
{
int size(std::atoi(argv[1]));
#ifdef EMPLACE
std::vector<Counter> v;
v.reserve(size);
for(int i = size; i>0 ; --i)
{
v.emplace_back(0);
}
#else
std::vector<Counter> v(size,0);
#endif
std::printf("Copies: %d , Construct: %d, Equal copies %d",Counter::count_copy, Counter::count_ctor, Counter::count_equal);
return 0;
}