Push_back
とemplace_back
の違いについて少し混乱しています。
void emplace_back(Type&& _Val);
void Push_back(const Type& _Val);
void Push_back(Type&& _Val);
右辺値参照を取るPush_back
オーバーロードがあるので、私はemplace_back
の目的が何になるかについてよくわかりませんか?
訪問者が言ったことに加えて:
MSCV10で提供されている関数void emplace_back(Type&& _Val)
は、遵守していない冗長なものです。これは、既に述べたように、Push_back(Type&& _Val)
と厳密に等価であるためです。
しかし、実際のC++ 0x形式のemplace_back
は本当に便利です。 void emplace_back(Args&&...)
;
value_type
をとる代わりに、可変個の引数リストを取るので、引数を完全に転送し、一時的にオブジェクトを作成せずにオブジェクトを直接コンテナに構築できます。
RVOとムーブの意味がどれほど巧妙に表にもたらされても、Push_backが不要なコピーを作成(または移動)する可能性がある複雑なケースがまだあるため、これは便利です。例えば、std::map
の伝統的なinsert()
関数では、一時的なものを作成しなければなりません。そして、それは次にstd::pair<Key, Value>
にコピーされます。
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);
では、なぜ彼らはMSVCで正しいバージョンのemplace_backを実装しなかったのでしょうか。実は、それは少し前に私を悩ませたので、 Visual C++ブログ で同じ質問をしました。 MicrosoftのVisual C++標準ライブラリ実装の公式メンテナであるStephan T Lavavejからの回答です。
Q:現在ベータ2のemplace関数は、ある種のプレースホルダーにすぎませんか?
A:ご存知のとおり、可変テンプレートはVC10では実装されていません。
make_shared<T>()
、Tuple、<functional>
の新しいものなどのプリプロセッサ機構を使ってそれらをシミュレートします。このプリプロセッサ機構は、使用および保守が比較的困難です。また、サブヘッダを繰り返し含める必要があるため、コンパイル速度にも大きく影響します。時間的制約とコンパイル速度の問題の組み合わせのため、emplace関数では可変テンプレートをシミュレートしていません。可変テンプレートがコンパイラに実装されると、emplace関数を含むライブラリでそれらを利用することが期待できます。我々は非常に真剣に順応をとります、しかし残念なことに、我々はすべてを一度にすることができません。
それは理解できる決定です。さまざまなテンプレートをプリプロセッサで恐ろしいトリックでエミュレートしようとしたことがある人はだれでも、このことがどれほど不快であるかを知っています。
emplace_back
はvector::value_type
型の引数をとるべきではなく、代わりに追加項目のコンストラクタに転送される可変引数を取るべきです。
template <class... Args> void emplace_back(Args&&... args);
コピーコンストラクタに転送されるvalue_type
を渡すことは可能です。
それは引数を転送するので、これはあなたが右辺値を持っていないなら、これはまだコンテナが移動されたコピーではなく「コピーされた」コピーを保存することを意味します。
std::vector<std::string> vec;
vec.emplace_back(std::string("Hello")); // moves
std::string s;
vec.emplace_back(s); //copies
しかし、上記はPush_back
がすることと同じであるべきです。それはおそらくむしろ以下のようなユースケースのためのものです。
std::vector<std::pair<std::string, std::string> > vec;
vec.emplace_back(std::string("Hello"), std::string("world"));
// should end up invoking this constructor:
//template<class U, class V> pair(U&& x, V&& y);
//without making any copies of the strings
emplace_back
の最適化は次の例で実証できます。
emplace_back
コンストラクタの場合、A (int x_arg)
が呼び出されます。そしてPush_back
の場合、A (int x_arg)
が最初に呼び出され、その後move A (A &&rhs)
が呼び出されます。
もちろん、コンストラクタはexplicit
としてマークされる必要がありますが、現在の例では明示性を取り除くのに良いです。
#include <iostream>
#include <vector>
class A
{
public:
A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }
A () { x = 0; std::cout << "A ()\n"; }
A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }
A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }
private:
int x;
};
int main ()
{
{
std::vector<A> a;
std::cout << "call emplace_back:\n";
a.emplace_back (0);
}
{
std::vector<A> a;
std::cout << "call Push_back:\n";
a.Push_back (1);
}
return 0;
}
出力:
call emplace_back:
A (x_arg)
call Push_back:
A (x_arg)
A (A &&)
emplace_back
準拠の実装は、ベクトルに追加されると引数をvector<Object>::value_type
コンストラクタに転送します。私はVisual Studioが可変テンプレートをサポートしていなかったことを思い出します、しかし可変テンプレートではVisual Studio 2013 RCでサポートされるので、私は適合署名が追加されると思います。
emplace_back
では、引数を直接vector<Object>::value_type
コンストラクターに直接転送する場合、厳密に言えば、emplace_back
関数のために移動可能またはコピー可能な型は必要ありません。 vector<NonCopyableNonMovableObject>
の場合、vector<Object>::value_type
は成長するためにコピー可能または移動可能な型を必要とするので、これは役に立ちません。
しかし note これはstd::map<Key, NonCopyableNonMovableObject>
には便利かもしれません、一度エントリをマップに割り当てたら、vector
とは違ってもう移動やコピーをする必要がないので、std::map
を有効に使えます。コピー可能でも移動可能でもないマップ型。
Push_backとemplace_backのためのいいコードがここに示されています。
http://en.cppreference.com/w/cpp/container/vector/emplace_back
移動操作は、emplace_backではなくPush_backで見ることができます。
リストの場合はもう1つ:
//要素をその場で構築します。
emplace_back( "element");
//新しいオブジェクトを作成してから、引数の値をコピー(または移動)します。 Push_back(explicitDataType {"element"});