Can I break a valid C++03 program by replacing std::vector::push_back
with emplace_back
and compiling it with C++ 11 compiler? From reading emplace_back
reference I gather it shouldn't happen, but I'll admit I don't fully get rvalue references.

- 169,198
- 16
- 310
- 405

- 32,368
- 48
- 194
- 335
6 Answers
I constructed a short example that actually fails to compile when push_back
is replaced by emplace_back
:
#include <vector>
struct S {
S(double) {}
private:
explicit S(int) {}
};
int main() {
std::vector<S>().push_back(0); // OK
std::vector<S>().emplace_back(0); // error!
}
The call to push_back
needs to convert its argument 0
from type int
to type S
. Since this is an implicit conversion, the explicit constructor S::S(int)
is not considered, and S::S(double)
is called. On the other hand, emplace_back
performs direct initialization, so both S::S(double)
and S::S(int)
are considered. The latter is a better match, but it's private
, so the program is ill-formed.

- 111,498
- 10
- 176
- 312
-
2Indeed, clever. When I first looked at this question I thought to myself, "well, `emplace_back` can call explicit constructors". Then I foolishly thought, "but that's a breaking change when moving from `emplace_back` to `push_back`, not the other way around". You can do the same thing with a situation where the `emplace_back` call becomes ambiguous where `push_back` is not: `struct S { S(double) {} explicit S(float) {} };`, but of course that's a *terrible* set of constructors to give `S`. – Steve Jessop Feb 28 '14 at 00:38
-
So does that mean as long as the code can be successfully compiled, there's no worry to shift to emplace_back? – BrandonSun Dec 30 '16 at 03:30
Yes, you can change the behavior (more than just avoiding a copy constructor call), since emplace_back
only sees imperfectly forwarded arguments.
#include <iostream>
#include <vector>
using namespace std;
struct Arg { Arg( int ) {} };
struct S
{
S( Arg ) { cout << "S(int)" << endl; }
S( void* ) { cout << "S(void*)" << endl; }
};
auto main()
-> int
{
vector<S>().ADD( 0 );
}
Example builds:
[H:\dev\test\0011] > g++ foo.cpp -D ADD=emplace_back && a S(int) [H:\dev\test\0011] > g++ foo.cpp -D ADD=push_back && a S(void*) [H:\dev\test\0011] > _
Addendum: as pointed out by Brian Bi in his answer, another difference that can lead to different behavior is that a push_back
call involves an implicit conversion to T
, which disregards explicit
constructors and conversion operators, while emplace_back
uses direct initialization, which does consider also explicit
constructors and conversion operators.

- 1
- 1

- 142,714
- 15
- 209
- 331
-
Could you explain in more detail how this works? I tried to figure it out myself but I'm really intrigued. – Brian Bi Feb 28 '14 at 00:03
-
2@BrianBi: you can't have more than one user-defined conversion in a conversion sequence. So `null pointer constant` -> `void*` -> `S` is the only available conversion with `push_back`. With `emplace_back`, the fact that `0` is a null pointer constant is lost because the template parameter pack can't capture that, it just captures that it's an `int` (hence "imperfectly forwarded"). Furthermore there's no need for two user-defined conversions in one sequence because we're explicitly constructing an `S`. So the `Arg` constructor is selected for `S`, with an implicit conversion `int` -> `Arg`. – Steve Jessop Feb 28 '14 at 00:13
-
@SteveJessop: thanks, as it turned out i didn't 100% understand my own example code until i read your reply to Brian. :-) – Cheers and hth. - Alf Feb 28 '14 at 00:33
The emplace
versions don't create an object of the desired type at all under exception circumstances. This can lead to a bug.
Consider the following example, which uses std::vector
for simplicity (assume uptr
behaves like std::unique_ptr
, except the constructor is not explicit):
std::vector<uptr<T>> vec;
vec.push_back(new T());
It is exception-safe. A temporary uptr<T>
is created to pass to push_back
, which is moved into the vector. If reallocation of the vector fails, the allocated T
is still owned by a smart pointer which correctly deletes it.
Compare to:
std::vector<uptr<T>> vec;
vec.emplace_back(new T());
emplace_back
is not allowed to create a temporary object. The ptr
will be created once, in-place in the vector. If reallocation fails, there is no location for in-place creation, and no smart pointer will ever be created. The T
will be leaked.
Of course, the best alternative is:
std::vector<std::unique_ptr<T>> vec;
vec.push_back(make_unique<T>());
which is equivalent to the first, but makes the smart pointer creation explicit.

- 277,958
- 43
- 419
- 720
If you don't have crazy side-effects in copy constructor of the objects that you hold in your vector, then no.
emplace_back
was introduced to optimise-out unnecessary copying and moving.

- 1,925
- 1
- 13
- 11
-
Also note that several of those optimizations were _also_ applied to `push_back`. – Mooing Duck Feb 27 '14 at 22:03
-
Yep, but compiler *has to be sure* that constructors are trivial, and by doing so it won't change overall result – Kissiel Feb 27 '14 at 22:05
-
I was thinking of capture by move, push_back is now move aware. Not sure what you're thinking I meant. – Mooing Duck Feb 27 '14 at 22:11
-
I meant that compiler cannot substitute copy, that is done using push_back with move, unless there is no side-effect. Or am I misunderstanding something? – Kissiel Feb 27 '14 at 22:16
-
I _think_ this is just a communication error, and that we're in agreement. I was merely observing that `push_back` now does less copying than it did in C++03 due to rvalues. – Mooing Duck Feb 27 '14 at 22:26
-
1This answer is technically wrong (an example is given in my answer), but as a matter of practice you would have to be haunted by mr. Murphy in order for the code to be broken. – Cheers and hth. - Alf Feb 27 '14 at 22:49
Suppose a user-defined class could be initialized from braced-initializer. e.g.
struct S {
int value;
};
then
std::vector<S> v;
v.push_back({0}); // fine
v.emplace_back({0}); // template type deduction fails
std::vector::emplace_back
is template function but std::vector::push_back
is non-template function. With a braced-initializer std::vector::emplace_back
would fail because template argument deduction fails.
Non-deduced contexts
6) The parameter P, whose A is a braced-init-list, but P is not
std::initializer_list
or a reference to one:

- 169,198
- 16
- 310
- 405
int main() {
std::vector<S>().push_back(0);
std::vector<S>().emplace_back(0);
}
Give struct constructor in emplace_back i.e The above code will be like this
int main() {
std::vector<S>().push_back(0);
std::vector<S>().emplace_back(S(0));
}