我在工作中看到很多代码,人们使用emplace和emplace_back和临时对象,如下所示:
struct A {
A::A(int, int);
};
vector<A> v;
vector<A>.emplace_back(A(1, 2));
我知道emplace_back的重点是能够直接传递参数,如下所示:
v.emplace_back(1, 2);
但遗憾的是,有些人并不清楚这一点。但是,我们不要纠缠于此......
我的问题是:编译器是否能够对此进行优化并跳过创建和复制?或者我应该真的尝试解决这些问题吗?
供您参考......我们正在使用C ++ 14。
答案 0 :(得分:13)
我的问题是:编译器是否能够对此进行优化并跳过创建和复制?或者我应该真的尝试解决这些问题吗?
在一般情况下,它无法避免复制。由于emplace_back
通过转发引用接受,它必须从纯粹的标准角度创建临时值。毕竟,这些引用必须绑定到对象。
复制省略是一组规则,允许避免复制(或移动)构造函数,并且复制省略,即使构造函数和相应的析构函数具有副作用。它仅适用于特定情况。通过引用传递参数不是其中之一。因此,对于非平凡类型,其中对象副本不能由as-if规则内联,如果编译器的目标是符合标准,则编译器的指针是绑定的。
答案 1 :(得分:4)
编译器能够对此进行优化并跳过创建和复制吗?
不一定涉及副本。如果移动构造函数可用,则会有移动。这无法进行优化,因为直接初始化情况只会调用init构造函数,而在另一种情况下,将另外调用移动构造函数(包括其副作用)。
因此,如果可能,您应该重构这些代码。
答案 2 :(得分:2)
简单的答案是否定的; elision不能完美转发。但这是c++,所以答案实际上是肯定的。
需要一些样板:
struct A {
A(int, int){std::cout << "A(int,int)\n"; }
A(A&&){std::cout<<"A(A&&)\n";}
};
template<class F>
struct maker_t {
F f;
template<class T>
operator T()&&{ return f(); }
};
template<class F>
maker_t<std::decay_t<F>> maker( F&& f ) { return {std::forward<F>(f)}; }
vector<A> v;
v.emplace_back(maker([]{ return A(1,2); }));
输出是对A(int,int)
的一次调用。没有动作。在c++17中,制作甚至不需要移动构造函数存在(但是矢量确实如此,因为它认为它可能必须移动已经分配的缓冲区中的元素)。在c++14中,这些动作都被简化了。
答案 3 :(得分:1)
我只想添加
Jon Kalb有一个关于复制省略和RVO的5分钟闪电谈话 https://youtu.be/fSB57PiXpRw
此外,使用不同的编译器gcc,clang或icc
可能会得到不同的结果请参阅编译器资源管理器,尝试不同的编译器和设置并亲自查看