41

C++ 11以降、どのような状況でパラメーターで const 参照を使用する必要があるのか​​ 疑問に思っていました。移動のセマンティクスを完全には理解していませんが、これは正当な質問だと思います。この質問は、値を「読み取る」ことだけが必要な場合に、作成中のコピーが const 参照に置き換えられる状況 (例: const メンバー関数の使用) のみを対象としています。

通常、私は次のような(メンバー)関数を書きます:

#include <vector>

template<class T>
class Vector {
    std::vector<T> _impl;
public:
    void add(const T& value) {
         _impl.push_back(value);
    }
};

class Tしかし、次のように記述し、もちろん移動コンストラクターを実装する場合、コンパイラーが移動セマンティクスを使用して最適化すると想定するのは安全だと考えています。

#include <vector>

template<class T>
class Vector {
    std::vector<T> _impl;
public:
    void add(T value) {
         _impl.push_back(value);
    }
};

私は正しいですか?もしそうなら、それはどのような状況でも使用できると仮定しても安全ですか? そうでない場合は、どれか知りたいです。たとえば、基本的な型のクラスの特殊化を実装する必要がないため、見た目がずっときれいになるだけでなく、これにより作業がはるかに簡単になります。

4

1 に答える 1

46

あなたが提案する解決策:

void add(T value) {
     _impl.push_back(value);
}

valueに右辺値を渡してもadd()(左辺値を渡す場合は 2 つのコピー)、この方法では常に の 1 つのコピーを実行することになるため、多少の調整が必要になりますvalue。に引数として渡しますpush_back

代わりに、次のようにする必要があります。

void add(T value) {
     _impl.push_back(std::move(value));
//                   ^^^^^^^^^
}

Tこれは優れていますが、移動するのが安いか高いかがわからないため、テンプレート コードにはまだ十分ではありません。T次のような POD の場合:

struct X
{
    double x;
    int i;
    char arr[255];
};

その場合、移動はコピーよりも高速ではありません (実際、移動はコピーと同じことです)。ジェネリック コードは不要な操作を回避する必要があるため (これらの操作は一部の型ではコストがかかる可能性があるため)、パラメーターを値で取得する余裕はありません。

考えられる解決策の 1 つ (C++ 標準ライブラリで採用されているもの) は、 の 2 つのオーバーロードを提供することです。1add()つは左辺値参照を取り、もう 1 つは右辺値参照を取ります。

void add(T const& val) { _impl.push_back(val); }
void add(T&& val) { _impl.push_back(std::move(val)); }

もう 1 つの可能性は、(おそらく SFINAE 制約付きの) の完全転送テンプレート バージョンを提供することですadd()。これは、いわゆるユニバーサル リファレンス(Scott Meyers によって造られた非標準的な用語) を受け入れます。

template<typename U>
void add(U&& val) { _impl.push_back(std::forward<U>(val)); }

これらのソリューションは両方とも、左辺値が提供されたときに 1 つのコピーのみが実行され、右辺値が提供されたときに 1 つの移動のみが実行されるという意味で最適です。

于 2013-07-14T22:24:50.703 に答える