2016-02-27 16 views
12

このオブジェクトへの参照は、assignment operator overloadingでよく使用されます。これは、named parameters idiomのベースとしても使用され、setterメソッドへの呼び出しのチェーンごとにオブジェクトを初期化することができます。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; 
} 
+5

これらの両方の使用は安全です。この問題は、呼び出し元のコードが現在の文より長い参照を保持しようとした場合にのみ発生します。 'auto&ref = MakeObj(5).Me();'。はい、あなたは一時的なオブジェクトをこのように指定する左辺値を作成することができます(これは、用語「一時オブジェクト」と「右辺値」を交換可能に使用する人々をよく訂正する1つの理由です)。 –

+0

標準ライブラリでも* ://www.cplusplus.com/reference/vector/vector/operator=/: "戻り値:* this"標準ライブラリが誤った構造を使用すると、それは不思議です。 :) –

答えて

7

はい、*これを返すために安全です。簡単なケースは、これが一時的ではないにもかかわらず、可能であっても可能であることです:

一時的なオブジェクトは、それらが作成されたポイント。これは、たとえその評価が例外をスローすることで終了したとしても当てはまります(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; 
+0

このような過負荷を指摘していただきありがとうございます。それはどういう名前ですか?どのバージョンのC++標準がこれを導入しましたか? –

+2

私はそれがC++ 11だと思いますが、少なくともMSVC2013はサポートしていません。 私はhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2439.htmで読むことができます。私はthis-ptrのref-qualifierで関数のオーバーロードを呼び出していますが、正式な名前を知らない。 – JVApen

3

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

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

一時的な有効期間は、参照にバインドされている場合、一部の条件でも延長されることがあります。しかし、ここで奇跡を期待しないでください。ステートメント(住所を取るか参照を割り当てることによってf.ex)を超えて参照を保持しようとすると、すぐにUB(demo)につながる可能性があります。

constオブジェクトでこの種の構造体を使用する場合は、非const refを返そうとするといくつかの問題があります(ただし、割り当てや設定の例では関係ありません)。

+1

'const Obj&Me()constを追加する(return * this; } 'はオーバーロードとしてconstオブジェクトのサポートを追加します。 – StellarVortex

+0

@StellarVortexはい、exacly!私は、一般的には、* thisを参照のために返すときには、一般的な操作や非constの操作を注意深く考慮しなければならないという注意を喚起したいと思います。代入演算子とセッターのためにもちろんこれは問題ではない;-) – Christophe

4
// 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; 
+0

@ M.M:ありがとう、私は例を追加しました。 –

関連する問題