web-dev-qa-db-ja.com

*これを参照として返すのは安全ですか?

このオブジェクトへの参照を返すことは、 代入演算子のオーバーロード でよく使用されます。また、 名前付きパラメーターイディオム のベースとしても使用されます。これにより、セッターメソッドへの一連の呼び出しによってオブジェクトを初期化できます。Params().SetX(1).SetY(1)それぞれが* thisへの参照を返します。

しかし、*thisへの参照を返すのは正しいですか。一時オブジェクトのこれへの参照を返すメソッドを呼び出すとどうなりますか?

#include <iostream>

class Obj
{
public:
    Obj(int n): member(n) {}
    Obj& Me() { return *this; }

    int member;
};

Obj MakeObj(int n)
{
    return Obj(n);
}

int main()
{
    // Are the following constructions are correct:
    std::cout << MakeObj(1).Me().member << std::endl;
    std::cout << Obj(2).Me().member << std::endl;
    Obj(3).Me() = Obj(4);

    return 0;
}
19
anton_rh

はい、* thisを返品しても安全です。簡単なケースは、これが一時的なものではない場合ですが、一時的なものであっても、これは可能であるはずです。

一時オブジェクトは、(字句的に)作成されたポイントを含む完全式(1.9)を評価する最後のステップとして破棄されます。これは、その評価が例外のスローで終了した場合でも当てはまります(C++03§12.2/ 3)。

言い換えれば、セミコロンに到達するまで、すべてが正常であるはずです(理論的には)。

したがって、次のコードが機能するはずです。

std::cout << MakeObj(1).Me().member << std::endl;

これは機能しないはずですが:

const Obj &MakeMeObj(int n) { return Obj(n).Me(); }
std::cout << MakeMeObj(1).member << std::endl;

一時への参照を返すため、これは論理的です。ほとんどのコンパイラはこれについて警告/エラーを出しますが、コードが複雑になった場合、これは注意が必要です。

個人的には、一時オブジェクトでこれらのメソッドを呼び出して、APIユーザーにオブジェクトの存続期間について考えさせることは避けたいと思います。これは、メソッドをオーバーロードすることで実行できます:(コンパイラがすでにサポートしている場合)

Obj &Me() & { return *this; }
Obj &Me() && = delete;
8
JVApen
// Are the following constructions are correct:
std::cout << MakeObj(1).Me().member << std::endl;
std::cout << Obj(2).Me().member << std::endl;

はい。各行で、すべての一時オブジェクトの有効期間が延長され、完全な式が考慮されるためです。

cppreference.com のように:

(...)すべての一時オブジェクトは、(字句的に)作成されたポイントを含む完全な式を評価する最後のステップとして破棄されます(...)。

式全体を分割しようとすると、(うまくいけば)コンパイラエラーまたは警告が表示されます。

// not allowed:
Obj& ref = MakeObj(1);
std::cout << ref.Me().member << std::endl;

また、コンパイラが問題を認識し、診断メッセージを表示せずに実行可能ファイルを作成し、最終的に未定義の動作をプログラムに組み込むほど賢くない場合もあります。

// undefined behaviour:
Obj &ref = MakeObj(1).Me();
std::cout << ref.member << std::endl;
5
Christian Hackl

はい、これは安全です。一時オブジェクトの存続期間は、ステートメントの終わりまでです(より正確には、オブジェクトが作成された完全な式の評価)。これは、標準によって保証されています。

12.2/3:一時オブジェクトは、(字句的に)作成されたポイントを含む完全な式を評価する最後のステップとして破棄されます。

一時的な寿命は、参照にバインドされている場合、特定の条件下で延長されることさえあります。しかし、ここで奇跡を期待しないでください。ステートメントを超えて参照を保持しようとすると(たとえば、アドレスを取得するか、参照を割り当てることによって)、すぐにUB( demo )につながる可能性があります。

constオブジェクトでこの種の構造を使用する場合、非const refを返そうとするため、いくつかの問題も発生します(ただし、これは例では関係ありません)。割り当てとセッター用)。

4
Christophe