push_back
не более эффективен, и наблюдаемые вами результаты обусловлены изменением размеров самого вектора.
Когда вы вызываете emplace
после push_back
, вектор имеет изменить размер, чтобы освободить место для второго элемента. Это означает, что он должен переместить A
, который изначально находился внутри вектора, делая emplace
более сложным.
Если вы заранее зарезервируете достаточно места в векторе, этого не произойдет. Обратите внимание на вызов va.reserve(2)
после создания va
:
#include <iostream>
#include <vector>
class A
{
public:
A() {std::cout << "A const" << std::endl;}
~A() {std::cout << "A dest" << std::endl;}
A(const A& a) {std::cout << "A copy const" << std::endl;}
A(A&& a) {std::cout << "A move const" << std::endl;}
A& operator=(const A& a) {std::cout << "A copy operator=" << std::endl; return *this; }
A& operator=(A&& a) {std::cout << "A move operator=" << std::endl; return *this; }
};
int main () {
std::vector<A> va;
// Now there's enough room for two elements
va.reserve(2);
std::cout <<"push:" << std::endl;
va.push_back(A());
std::cout <<std::endl<< "emplace:" << std::endl;
va.emplace_back(A());
std::cout <<std::endl<< "end:" << std::endl;
return 0;
}
Соответствующий вывод:
push:
A const
A move const
A dest
emplace:
A const
A move const
A dest
end:
A dest
A dest
Можем ли мы сделать вещи еще более эффективными? Да! emplace_back
принимает любые аргументы, которые вы ему предоставили, и перенаправляет их в конструктор A
. Поскольку A
имеет конструктор, который не принимает аргументов, вы также можете использовать emplace_back
без аргументов! Другими словами, мы меняем
va.emplace_back(A());
на
va.emplace_back(); // No arguments necessary since A is default-constructed
Это не приводит ни к копированию, ни к перемещению:
push:
A const
A move const
A dest
emplace:
A const
end:
A dest
A dest
Примечание по изменение размеров векторов: Важно отметить, что реализация std::vector
умна. Если бы A
был тривиально копируемым типом, std::vector
мог бы изменить размер на месте без дополнительного копирования с помощью системной функции, подобной realloc
. Однако, поскольку конструкторы и разрушения A
содержат код, realloc
здесь использовать нельзя.