The main reason is that classic C casts make no distinction between what we call static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
, and dynamic_cast<>()
. These four things are completely different.
A static_cast<>()
is usually safe. There is a valid conversion in the language, or an appropriate constructor that makes it possible. The only time it's a bit risky is when you cast down to an inherited class; you must make sure that the object is actually the descendant that you claim it is, by means external to the language (like a flag in the object). A dynamic_cast<>()
is safe as long as the result is checked (pointer) or a possible exception is taken into account (reference).
A reinterpret_cast<>()
(or a const_cast<>()
) on the other hand is always dangerous. You tell the compiler: "trust me: I know this doesn't look like a foo
(this looks as if it isn't mutable), but it is".
The first problem is that it's almost impossible to tell which one will occur in a C-style cast without looking at large and disperse pieces of code and knowing all the rules.
Let's assume these:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
Now, these two are compiled the same way:
CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
// Safe; as long as we checked
// but harder to read
However, let's see this almost identical code:
CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert
pOther = (CMyOtherStuff*)(pSomething); // No compiler error.
// Same as reinterpret_cast<>
// and it's wrong!!!
As you can see, there is no easy way to distinguish between the two situations without knowing a lot about all the classes involved.
The second problem is that the C-style casts are too hard to locate. In complex expressions it can be very hard to see C-style casts. It is virtually impossible to write an automated tool that needs to locate C-style casts (for example a search tool) without a full blown C++ compiler front-end. On the other hand, it's easy to search for "static_cast<" or "reinterpret_cast<".
pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
// No compiler error.
// but the presence of a reinterpret_cast<> is
// like a Siren with Red Flashing Lights in your code.
// The mere typing of it should cause you to feel VERY uncomfortable.
That means that, not only are C-style casts more dangerous, but it's a lot harder to find them all to make sure that they are correct.
In addition to what visitor said :
The function void emplace_back(Type&& _Val)
provided by MSCV10 is non conforming and redundant, because as you noted it is strictly equivalent to push_back(Type&& _Val)
.
But the real C++0x form of emplace_back
is really useful: void emplace_back(Args&&...)
;
Instead of taking a value_type
it takes a variadic list of arguments, so that means that you can now perfectly forward the arguments and construct directly an object into a container without a temporary at all.
That's useful because no matter how much cleverness RVO and move semantic bring to the table there is still complicated cases where a push_back is likely to make unnecessary copies (or move). For example, with the traditional insert()
function of a std::map
, you have to create a temporary, which will then be copied into a std::pair<Key, Value>
, which will then be copied into the map :
std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";
// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString)));
// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);
So why didn't they implement the right version of emplace_back in MSVC? Actually, it bugged me too a while ago, so I asked the same question on the Visual C++ blog. Here is the answer from Stephan T Lavavej, the official maintainer of the Visual C++ standard library implementation at Microsoft.
Q: Are beta 2 emplace functions just some kind of placeholder right now?
A: As you may know, variadic templates
aren't implemented in VC10. We
simulate them with preprocessor
machinery for things like
make_shared<T>()
, tuple, and the new
things in <functional>
. This
preprocessor machinery is relatively
difficult to use and maintain. Also,
it significantly affects compilation
speed, as we have to repeatedly
include subheaders. Due to a
combination of our time constraints
and compilation speed concerns, we
haven't simulated variadic templates
in our emplace functions.
When variadic templates are
implemented in the compiler, you can
expect that we'll take advantage of
them in the libraries, including in
our emplace functions. We take
conformance very seriously, but
unfortunately, we can't do everything
all at once.
It's an understandable decision. Everyone who tried just once to emulate variadic template with preprocessor horrible tricks knows how disgusting this stuff gets.
Best Answer
I have thought about this question quite a bit over the past four years. I have come to the conclusion that most explanations about
push_back
vs.emplace_back
miss the full picture.Last year, I gave a presentation at C++Now on Type Deduction in C++14. I start talking about
push_back
vs.emplace_back
at 13:49, but there is useful information that provides some supporting evidence prior to that.The real primary difference has to do with implicit vs. explicit constructors. Consider the case where we have a single argument that we want to pass to
push_back
oremplace_back
.After your optimizing compiler gets its hands on this, there is no difference between these two statements in terms of generated code. The traditional wisdom is that
push_back
will construct a temporary object, which will then get moved intov
whereasemplace_back
will forward the argument along and construct it directly in place with no copies or moves. This may be true based on the code as written in standard libraries, but it makes the mistaken assumption that the optimizing compiler's job is to generate the code you wrote. The optimizing compiler's job is actually to generate the code you would have written if you were an expert on platform-specific optimizations and did not care about maintainability, just performance.The actual difference between these two statements is that the more powerful
emplace_back
will call any type of constructor out there, whereas the more cautiouspush_back
will call only constructors that are implicit. Implicit constructors are supposed to be safe. If you can implicitly construct aU
from aT
, you are saying thatU
can hold all of the information inT
with no loss. It is safe in pretty much any situation to pass aT
and no one will mind if you make it aU
instead. A good example of an implicit constructor is the conversion fromstd::uint32_t
tostd::uint64_t
. A bad example of an implicit conversion isdouble
tostd::uint8_t
.We want to be cautious in our programming. We do not want to use powerful features because the more powerful the feature, the easier it is to accidentally do something incorrect or unexpected. If you intend to call explicit constructors, then you need the power of
emplace_back
. If you want to call only implicit constructors, stick with the safety ofpush_back
.An example
std::unique_ptr<T>
has an explicit constructor fromT *
. Becauseemplace_back
can call explicit constructors, passing a non-owning pointer compiles just fine. However, whenv
goes out of scope, the destructor will attempt to calldelete
on that pointer, which was not allocated bynew
because it is just a stack object. This leads to undefined behavior.This is not just invented code. This was a real production bug I encountered. The code was
std::vector<T *>
, but it owned the contents. As part of the migration to C++11, I correctly changedT *
tostd::unique_ptr<T>
to indicate that the vector owned its memory. However, I was basing these changes off my understanding in 2012, during which I thought "emplace_back
does everythingpush_back
can do and more, so why would I ever usepush_back
?", so I also changed thepush_back
toemplace_back
.Had I instead left the code as using the safer
push_back
, I would have instantly caught this long-standing bug and it would have been viewed as a success of upgrading to C++11. Instead, I masked the bug and didn't find it until months later.