Скопировать elision при создании объекта внутри emplace()

Я вижу много кода на работе, где люди используют 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);

Но, к сожалению, это не ясно некоторым людям. Но не останавливайтесь на этом....

Мой вопрос: компилятор способен оптимизировать это и пропустить создание и копирование? Или я действительно пытаюсь исправить эти события?

Для вашей справки... мы работаем с С++ 14.

Ответ 1

Мой вопрос: компилятор способен оптимизировать это и пропустить создание и копирование? Или я действительно пытаюсь исправить эти события?

В общем случае он не может избежать копирования. Поскольку emplace_back принимает пересылку ссылок, он должен создавать временные точки с чистой стандартной точки зрения. В конце концов, эти ссылки должны привязываться к объектам.

Копирование elision - это набор правил, который позволяет избежать конструктора копирования (или перемещения), и копия исчезла, даже если конструктор и соответствующий деструктор имеют побочные эффекты. Он применяется только в особых обстоятельствах. И передача аргументов по ссылке не одна из них. Таким образом, для нетривиальных типов, когда копии экземпляра не могут быть встроены в правило as-if, руки компилятора связаны, если они нацелены на стандартное соответствие.

Ответ 2

компилятор способен оптимизировать это и пропустить создание и копирование?

Существует не обязательно копия. Если конструктор перемещения доступен, будет движение. Это невозможно оптимизировать, так как случай прямой инициализации просто вызовет конструктор init, в то время как в другом случае конструктор перемещения будет вызван дополнительно (включая его побочные эффекты).

Поэтому, если это возможно, вы должны реорганизовать эти коды.

Ответ 3

Легкий ответ - нет; 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); }));

живой пример.

Вывод - это один вызов A(int,int). Не происходит никакого движения. В создание даже не требует существования конструктора перемещения (но вектор делает, поскольку он считает, что ему, возможно, придется перемещать элементы в уже распределенном буфере). В движения просто исчезают.

Ответ 4

Я просто хочу добавить

Существует отличная 5 минут молниеносно о копировании элиции и RVO от Jon Kalb https://youtu.be/fSB57PiXpRw

Кроме того, вы можете получить разные результаты, используя разные компиляторы gcc, clang или icc

См. Проводник компилятора, попробуйте разные компиляторы и настройки и убедитесь сами.

https://godbolt.org/g/Yjo9qA