Reputation: 9085
In my C++ application I heavily use STL containers like vector
. There are a lot of calls to push_back
, and I have been concerned about unnecessary constructions and copy operations.
My application is pretty low-level and I am very concerned about CPU and memory usage. Should I replace all calls to push_back
with calls to emplace_back
?
I am using Visual Studio 2013.
Upvotes: 8
Views: 1802
Reputation: 218750
This test:
#include <type_traits>
#include <typeinfo>
#include <iostream>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>
#include <vector>
template <typename T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
template <int N>
struct member
{
member()
{
std::cout << type_name<member>() << "()\n";
}
~member()
{
std::cout << "~" << type_name<member>() << "()\n";
}
member(member const& x)
{
std::cout << type_name<member>()
<< "(" << type_name<decltype(x)>() << ")\n";
}
member& operator=(member const& x)
{
std::cout << type_name<member>() << "::operator=("
<< type_name<decltype(x)>() << ")\n";
return *this;
}
member(member&& x)
{
std::cout << type_name<member>()
<< "(" << type_name<decltype(x)>() << ")\n";
}
member& operator=(member&& x)
{
std::cout << type_name<member>() << "::operator=("
<< type_name<decltype(x)>() << ")\n";
return *this;
}
};
int
main()
{
std::vector<member<1>> v;
v.reserve(10);
member<1> m;
std::cout << "\npush_back an lvalue\n";
v.push_back(m);
std::cout << "\nemplace_back an lvalue\n";
v.emplace_back(m);
std::cout << "\npush_back an xvalue\n";
v.push_back(std::move(m));
std::cout << "\nemplace_back an xvalue\n";
v.emplace_back(std::move(m));
std::cout << "\npush_back a prvalue\n";
v.push_back(member<1>{});
std::cout << "\nemplace_back an prvalue\n";
v.emplace_back(member<1>{});
std::cout << "\nDone\n";
}
For me outputs:
member<1>()
push_back an lvalue
member<1>(member<1> const&)
emplace_back an lvalue
member<1>(member<1> const&)
push_back an xvalue
member<1>(member<1>&&)
emplace_back an xvalue
member<1>(member<1>&&)
push_back a prvalue
member<1>()
member<1>(member<1>&&)
~member<1>()
emplace_back an prvalue
member<1>()
member<1>(member<1>&&)
~member<1>()
Done
~member<1>()
~member<1>()
~member<1>()
~member<1>()
~member<1>()
~member<1>()
~member<1>()
I.e. I would not expect any difference whatsoever.
Upvotes: 2
Reputation: 9085
I replaced all calls to push_back
with calls to emplace_back
and noticed the following:
Based on these experiences I can highly recommend to make the move from push_back
to emplace_back
if your project does not need to be backwards-compatible with older compilers.
Upvotes: 6
Reputation: 8824
It is an almost always rule. You cannot rely on side effect of copy constructors so it should means that skipping it explicitly is the right thing to do, but there is one case.
std::vector<std::unique_ptr<A>> foo;
foo.emplace_back( new A );
If at some time a throw is triggered, like when the vector resize, you end with a leak. So emplace_back is not possible.
If A
constructors and the parameter you sent are exception safe, then there is no reason to not use an emplace_back.
Upvotes: 4