Скопировать 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 не работает с совершенной пересылкой. Но это 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 движения просто исчезают.
Ответ 4
Я просто хочу добавить
Существует отличная 5 минут молниеносно о копировании элиции и RVO от Jon Kalb https://youtu.be/fSB57PiXpRw
Кроме того, вы можете получить разные результаты, используя разные компиляторы gcc, clang или icc
См. Проводник компилятора, попробуйте разные компиляторы и настройки и убедитесь сами.
https://godbolt.org/g/Yjo9qA