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.
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.
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.
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.
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.
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_listor a reference to one:
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));
}
来源:https://stackoverflow.com/questions/22080290/are-there-any-cases-where-it-is-incorrect-to-replace-push-back-with-emplace-back