push_back
との違いについて少し混乱していemplace_back
ます。
void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);
右辺値参照をとる過負荷があるpush_back
ので、目的がどうなるかよくわかりませんemplace_back
か?
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とmoveセマンティックがテーブルにどれほど巧妙さをもたらしても、push_backが不要なコピー(または移動)を作成する可能性が高い複雑なケースがまだあるため、これは便利です。たとえば、の従来insert()
の機能ではstd::map
、一時的なものを作成する必要があります。一時的なものは、にコピーされ、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を実装しなかったのでしょうか。実際、それは私を少し前に悩ませたので、私はVisualC++ブログで同じ質問をしました。これは、MicrosoftのVisualC++標準ライブラリ実装の公式メンテナであるStephanTLavavejからの回答です。
Q:ベータ2エンプレース関数は、現在、ある種のプレースホルダーにすぎませんか?
A:ご存知かもしれませんが、可変個引数テンプレートはVC10には実装されていません。
make_shared<T>()
、タプル、およびの新しいもののプリプロセッサ機構を使用してそれらをシミュレートします<functional>
。このプリプロセッサ機構は、使用と保守が比較的困難です。また、サブヘッダーを繰り返し含める必要があるため、コンパイル速度に大きく影響します。時間の制約とコンパイル速度の懸念の組み合わせにより、emplace関数で可変個引数テンプレートをシミュレートしていません。可変個引数テンプレートがコンパイラーに実装されると、emplace関数を含むライブラリーでそれらを利用することが期待できます。私たちは適合性を非常に真剣に受け止めていますが、残念ながら、すべてを一度に行うことはできません。
それは理解できる決断です。プリプロセッサの恐ろしいトリックで可変個引数テンプレートをエミュレートしようとしたことのある人なら誰でも、このようなものがいかに嫌なものになるかを知っています。
emplace_back
タイプの引数を取るべきではありませんがvector::value_type
、代わりに、追加されたアイテムのコンストラクターに転送される可変個引数を取ります。
template <class... Args> void emplace_back(Args&&... args);
value_type
コピーコンストラクタに転送されるaを渡すことができます。
引数を転送するため、右辺値がない場合でも、コンテナは移動されたコピーではなく、「コピーされた」コピーを格納することを意味します。
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)
が呼び出されます。そして、for
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 &&)
リストのもう1つの例:
// constructs the elements in place.
emplace_back("element");
// creates a new object and then copies (or moves) that object.
push_back(ExplicitDataType{"element"});
特定のユースケースemplace_back
:一時オブジェクトを作成してコンテナにプッシュする必要がある場合は、のemplace_back
代わりにを使用しpush_back
ます。コンテナ内のインプレースでオブジェクトを作成します。
ノート:
push_back
上記の場合、一時オブジェクトを作成し、それをコンテナに移動します。ただし、に使用されるインプレース構築はemplace_back
、オブジェクトを構築してから移動するよりもパフォーマンスが高くなります(通常はコピーが必要です)。emplace_back
代わりに、多くの問題なく使用できます。push_back
(例外を参照)push_backとemplace_backの優れたコードをここに示します。
http://en.cppreference.com/w/cpp/container/vector/emplace_back
移動操作は、emplace_backではなくpush_backで確認できます。
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
ただし、これはに役立つ可能性があることに注意std::map<Key, NonCopyableNonMovableObject>
してください。マップにエントリを割り当てると、とは異なり、移動またはコピーする必要がなくなります。つまり、コピーもコピーもできないマップされたタイプでも効果的にvector
使用できます。std::map
可動。