问题
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.
回答1:
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.
回答2:
I replaced all calls to push_back
with calls to emplace_back
and noticed the following:
- RAM usage is reduced by approximately 20% (update: this may have been due to other effects)
- CPU usage is unchanged
- The binary is slightly smaller (x64)
- There were no compatibility problems
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.
回答3:
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.
来源:https://stackoverflow.com/questions/22468837/should-i-replace-all-calls-to-push-back-with-emplace-back