在emplace()中创建对象时复制省略

时间:2018-06-07 12:02:04

标签: c++ c++14 copy-elision

我在工作中看到很多代码,人们使用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。

4 个答案:

答案 0 :(得分:13)

  

我的问题是:编译器是否能够对此进行优化并跳过创建和复制?或者我应该真的尝试解决这些问题吗?

在一般情况下,它无法避免复制。由于emplace_back通过转发引用接受,它必须从纯粹的标准角度创建临时值。毕竟,这些引用必须绑定到对象。

复制省略是一组规则,允许避免复制(或移动)构造函数,并且复制省略,即使构造函数和相应的析构函数具有副作用。它仅适用于特定情况。通过引用传递参数不是其中之一。因此,对于非平凡类型,其中对象副本不能由as-if规则内联,如果编译器的目标是符合标准,则编译器的指针是绑定的。

答案 1 :(得分:4)

  

编译器能够对此进行优化并跳过创建和复制吗?

不一定涉及副本。如果移动构造函数可用,则会有移动。这无法进行优化,因为直接初始化情况只会调用init构造函数,而在另一种情况下,将另外调用移动构造函数(包括其副作用)。

因此,如果可能,您应该重构这些代码。

答案 2 :(得分:2)

简单的答案是否定的; elision不能完美转发。但这是,所以答案实际上是肯定的。

需要一些样板:

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); }));

live example

输出是对A(int,int)的一次调用。没有动作。在中,制作甚至不需要移动构造函数存在(但是矢量确实如此,因为它认为它可能必须移动已经分配的缓冲区中的元素)。在中,这些动作都被简化了。

答案 3 :(得分:1)

我只想添加

Jon Kalb有一个关于复制省略和RVO的5分钟闪电谈话 https://youtu.be/fSB57PiXpRw

此外,使用不同的编译器gcc,clang或icc

可能会得到不同的结果

请参阅编译器资源管理器,尝试不同的编译器和设置并亲自查看

https://godbolt.org/g/Yjo9qA

相关问题