emplace back

Why emplace_back is faster than push_back?

  • since C++ 11 until C++17

    1
    2
    template<class... >
    void emplace_back(&&... args);
  • since C++17

    1
    2
    template<class... >
    reference emplace_back(&&... args);

Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container. The arguments args… are forwarded to the constructor as std::forward<Args>(args)....
就地构造
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.

Parameters:

args: arguments to forward to the constructor of the element

Type requirements

T (the container’s element type) must meet the requirements of MoveInsertable and EmplaceConstructible.

Return value
  • (none) (until C++17)
  • A reference to the inserted element. (since C++17)
Complexity

Amortized constant.

Exceptions

If an exception is thrown, this function has no effect (strong exception guarantee). If T’s move constructor is not noexcept and is not CopyInsertable into *this, vector will use the throwing move constructor. If it throws, the guarantee is waived and the effects are unspecified.
如果 move constructor is not noexcept,在发生空间不足需要扩容时,编译器会调用拷贝构造函数而不是移动构造函数,就是为了保证强的异常安全性。

Notes

Since reallocation may take place, emplace_back requires the element type to be MoveInsertable for vectors.

The specialization std::vector<bool> did not have emplace_back() member until C++14.

Example

The following code uses emplace_back to append an object of type President to a std::vector. It demonstrates how emplace_back forwards parameters to the President constructor and shows how using emplace_back avoids the extra copy or move operation required when using push_back.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <string>
#include <iostream>
struct President
{
std::string name;
std::string country;
int year;
President(std::string p_name, std::string p_country, int p_year)
: name(std::move(p_name)), country(std::move(p_country)), year(p_year)
{
std::cout << "I am being constructed.n";
}
President(President&& other)
: name(std::move(other.name)), country(std::move(other.country)), year(other.year)
{
std::cout << "I am being moved.n";
}
President& operator=(const President& other) = default;
};
int main()
{
std::vector<President> elections;
std::cout << "emplace_back:n";
elections.emplace_back("Nelson Mandela", "South Africa", 1994);
std::vector<President> reElections;
std::cout << "npush_back:n";
reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));
std::cout << "nContents:n";
for (President const& president: elections) {
std::cout << president.name << " was elected president of "
<< president.country << " in " << president.year << ".n";
}
for (President const& president: reElections) {
std::cout << president.name << " was re-elected president of "
<< president.country << " in " << president.year << ".n";
}
}

Output:

1
2
3
4
5
6
7
8
9
10
emplace_back:
I am being constructed.
push_back:
I am being constructed.
I am being moved.
Contents:
Nelson Mandela was elected president of South Africa in 1994.
Franklin Delano Roosevelt was re-elected president of the USA in 1936.

Note from stack overflow

push_back takes a container element and copies/moves it into the container. emplace_back takes arbitrary arguments and constructs from those a new container element. But if you pass a single argument that’s already of element type to emplace_back, you’ll just use the copy/move constructor anyway.

1
2
v.push_back(T(x, y, z));
v.emplace_back(x, y, z); // no temporary, directly construct T(x, y, z) in place

The key difference, however, is that emplace_back performs explicit conversions:

1
2
std::vector<std::unique_ptr<Foo>> v;
v.emplace_back(new Foo(1, 'x', true)); // constructor is explicit!

由于 unique_ptr 的构造函数是 explicit 的,即不允许隐式类型转换,所以从 raw pointer 到 unique pointer 的转换是显式的,其实也就是 in-place 构造。
其实上面这个例子更推荐的做法是,

1
v.push_back(std::make_unique<Foo>(1, 'x', true));

即通过 make_unique 从可变参数实例化出一个 Foo 对象,并实例化出一个 unique_ptr 对象指向它,并将 unique_ptr 对象移动构造到向量中。
emplace_back 的另外一个优雅用处是:

1
2
3
std::vector<std::thread> threads;
threads.emplace_back(do_work, 10, "foo"); // call do_work(10, "foo")
threads.emplace_back(&Foo::g, x, 20, false); // call x.g(20, false)