Are there any cases where it is incorrect to replace push_back with emplace_back?

Is it possible to split a valid C ++ 03 program by replacing std::vector::push_back with emplace_back and compiling it with a C ++ 11 compiler? From reading emplace_back link that I am collecting should not come about, but I admit that I am not fully getting the rvalue links.

+8
c ++ vector
source share
4 answers

I built a short example that does not actually compile when push_back is replaced with 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 push_back call should convert its argument 0 from int to type S Since this is an implicit conversion, the explicit constructor S::S(int) not considered and S::S(double) called. On the other hand, emplace_back performs direct initialization, therefore both S::S(double) and S::S(int) . The latter is better, but it is private , so the program is poorly formed.

+14
source share

Yes, you can change the behavior (more than just avoiding calling the copy constructor), since emplace_back only sees incorrectly redirected 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 ); } 

Examples of constructions:

 [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 Brian Bee pointed out in his answer , another difference that can lead to different behavior is that a push_back call includes an implicit conversion to T that ignores explicit constructors and conversion operators, whereas emplace_back uses direct initialization, which also considers explicit constructors and conversion operators.

+2
source share

If you don't have crazy side effects in the copy constructor of the objects you hold in your vector, then no.

emplace_back was introduced to optimize unnecessary copying and moving.

+1
source share

The emplace versions emplace not create an object of the required type at all under the circumstances of the exception. This may result in an error.

Consider the following example, which uses just std::vector for convenience (assume that uptr behaves like std::unique_ptr , except that the constructor is not explicit):

 std::vector<uptr<T>> vec; vec.push_back(new T()); 

This is safe for exceptions. A temporary uptr<T> is created to go to push_back , which moves to the vector. If the redistribution of the vector fails, the highlighted T still belongs to the smart pointer, which correctly deletes it.

Compare with:

 std::vector<uptr<T>> vec; vec.emplace_back(new T()); 

emplace_back not allowed to create a temporary object. ptr will be created once, in place in the vector. If redistribution fails, there is no place to create in place, and no smart pointer will ever be created. T will leak.

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 creation of a smart pointer explicit.

+1
source share

All Articles