1. ホーム
  2. c++

[解決済み] 参照渡しより値渡し、std::moveの方が優れている点

2022-04-26 18:17:57

質問

私は今C++を学んでいて、悪い習慣を身につけないようにしています。 私が理解したところでは、clang-tidyは多くの"ベストプラクティス"を含んでおり、私はできるだけそれらに固執しようとします(たとえ私が必ずしも理解していなくても なぜ しかし、ここで推奨されていることが理解できているかはわかりません。

チュートリアルにあったこのクラスを使ってみました。

class Creature
{
private:
    std::string m_name;

public:
    Creature(const std::string &name)
            :  m_name{name}
    {
    }
};

これは、clang-tidy からの提案で、参照ではなく値で渡すべきであり std::move . もしそうするならば、次のような提案を受けるでしょう。 name を参照にすること(毎回コピーされないようにするため)、および std::move は何の効果も持たないので nameconst ということで、削除したほうがいいですね。

警告が出ないのは const を完全に削除してください。

Creature(std::string name)
        :  m_name{std::move(name)}
{
}

の唯一の利点は、論理的であるように思えます。 const は、元の文字列をいじらないようにするためでした(値で渡したので、これは起こりません)。 しかし、私は次のように読みました。 CPlusPlus.com :

<ブロッククオート

ただし、標準ライブラリでは、移動は、移動元のオブジェクトを有効だが未指定の状態に残すことを意味する。つまり、このような操作の後、移動元のオブジェクトの値は破棄されるか、新しい値が割り当てられるだけであるべきで、それ以外のアクセスは不特定の値を生成します。

さて、こんなコードを想像してみてください。

std::string nameString("Alex");
Creature c(nameString);

なぜなら nameString は値で渡される。 std::move が無効になるのは name はコンストラクタの内部で使用され、元の文字列には触れません。しかし、これの利点は何でしょうか?コンテンツがコピーされるのは一度だけのような気がします。 m_name{name} 渡すときに値で渡す場合(そして移動される)。私は、これは値で渡すよりも良いことだと理解しています。 std::move (2回コピーされるから)。

そこで2つ質問です。

  1. ここで起こっていることは正しく理解できたでしょうか?
  2. を使うと何かいいことがあるのでしょうか? std::move を参照渡しで呼び出すよりも m_name{name} ?

解決方法は?

<ブロッククオート
  1. ここで起こっていることは正しく理解されましたか?

はい。

  1. を使用することの利点は何ですか? std::move を参照渡しで呼び出すよりも m_name{name} ?

この関数のシグネチャは、追加のオーバーロードを必要とせず、簡単に把握することができます。このシグネチャは、引数がコピーされることを即座に明らかにします。 const std::string& の参照がデータメンバとして格納され、後でぶら下がり参照になる可能性があります。また std::string&& nameconst std::string& 引数を使用することで、rvalue が関数に渡されたときに不要なコピーを避けることができます。lvalueの渡し方

std::string nameString("Alex");
Creature c(nameString);

を、引数を値で受け取る関数に渡すと、コピーと移動の構文が1つずつ発生します。同じ関数にrvalueを渡すと

std::string nameString("Alex");
Creature c(std::move(nameString));

は、2回の移動構文が発生します。これに対して、関数パラメータが const std::string& rvalueの引数を渡す場合でも、常にコピーが存在する。これは、引数の型が移動構築しにくいものである限り、明らかに有利である(例えば std::string ).

しかし、考慮すべきマイナス面もあります。関数の引数を(初期化するのではなく)別の変数に代入するような関数では、この理屈は成り立ちません。

void setName(std::string name)
{
    m_name = std::move(name);
}

というリソースの割り当てを解除することになります。 m_name は、再割り当てされる前に参照しています。Effective Modern C++のItem 41を読むことをお勧めしますし、また この質問 .