11

重複の可能性:
宣言された std::string の代わりにリテラルを返すとどうなりますか?

次のコードを検討してください

string getName () {
    return "meme";
}

string name = getName();

この関数getName()は一時オブジェクトを返します。C++03 では、stringgets のコピー コンストラクターが呼び出され、一時オブジェクトが破棄されることを理解しています。実際、コンパイラ (少なくとも GCC 4.7 では) は、オブジェクトを作成せずnameに一時オブジェクト自体に置き換え、一時オブジェクトを破棄しないことで、5 行目を最適化しているようです。MyVector( std::stringではなく、クラスで試しました)

C++11 標準で定義されているように、

  1. getName()右辺値を返していますか?

  2. 上記の 5 行目で、文字列のどのコンストラクターが呼び出されますか (移動またはコピー) ? std::move()移動コンストラクターが呼び出されるようにする 必要がありますか?

  3. 移動セマンティクスでは、コンパイラが提供する「コピー省略」最適化よりも効率が悪いですか?

4

1 に答える 1

19
  1. 関数は右辺値または左辺値を返しません。値のカテゴリは式に適用されます。したがって、関数を呼び出す式は、右辺値または左辺値である可能性があります。この場合、関数はオブジェクトを値で返すgetName()ため、式は右辺値式です。getNameこれは §5.2.2/10 からのものです。

    関数呼び出しは、結果の型が左辺値参照型または関数型への右辺値参照の場合は左辺値、結果の型がオブジェクト型への右辺値参照の場合は xvalue、それ以外の場合は prvalueです。

    関数の結果の型は左辺値または右辺値の参照ではないため、関数呼び出しは prvalue です。prvalue 式は、rvalue 式のサブセットです。

  2. move コンストラクターが使用されます (省略されている場合を除きます)。これgetName()は が右辺値であるため、右辺値参照を受け取る のコンストラクターがstd::string引数によりよく一致するためです。move 構築が省略された場合でも、move コンストラクタにアクセスできる必要があることに注意してください。つまり、省略されていなくても、コードはコンパイル可能でなければなりません。

  3. 一般に、コピーまたは移動の省略を最適化すると、コピーまたは移動が完全に取り除かれます。もちろん、実際に移動するよりも高速です。動きが省略された場合、文字通り何も起こりません。その移動に対して発行されるコードはありません。コンパイラは、コピー先または移動先の場所にオブジェクトを直接構築することでこれを実現します。

これも同様に最適化できることに注意してください。

string getName () {
  std::string str("meme");
  return str;
}
 
string name = getName();

ここでは、2 つの手が省略されます (名前付き戻り値の最適化として一般に知られているものを含みます)。ここで考慮すべき点が 2 つあります。まず、return str;コピー/移動省略の基準を満たしています (§12.8/31):

コピー省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます (複数のコピーを排除するために組み合わせることができます)。

  • クラスの戻り値の型を持つ関数の return ステートメントで、式が関数の戻り値の型と同じ cv-unqualified 型を持つ不揮発性自動オブジェクト (関数または catch-clause パラメーター以外) の名前である場合、自動オブジェクトを関数の戻り値に直接構築することにより、コピー/移動操作を省略できます
  • ...

2 つ目はstr、左辺値ですが、標準 (§12.8/32) で指定された特別なケースに適合するため、移動されることです。

コピー操作の省略の基準が満たされているか、ソース オブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値によって指定されているという事実を除いて満たされる場合、コピーのコンストラクターを選択するためのオーバーロードの解決は次のとおりです。オブジェクトが右辺値によって指定されたかのように最初に実行されます。

于 2012-12-23T15:49:55.350 に答える