私の質問(この後、長いイントロについて申し訳ありませんが、質問は太字で示されています)は、元々、Herb Sutters Exceptional C ++のアイテム23に触発されており、次のようなものがあります:
<snip>
...
int main()
{
GenericTableAlgorithm a( "Customer", MyWorker() );
a.Process();
}
と
class GenericTableAlgorithm
{
public:
GenericTableAlgorithm( const string& table,
GTAClient& worker );
bool Process();
private:
struct GenericTableAlgorithmImpl* pimpl_; //implementation
};
class GTAClient
{
///...
virtual bool ProcessRow( const PrimaryKey& ) =0;
//...
};
class MyWorker : public GTAClient
{
// ... override Filter() and ProcessRow() to
// implement a specific operation ...
};
</ snip>
さて、私はそのコードに関して次の問題を抱えています(そして、いいえ、C ++の専門家としてのSutter氏の腕前に疑いの余地はありません):
- GTAClient&workerは一時的なものをとることができない非定数参照であるため、このような例は機能しませんが、標準以前に記述されているか、タイプミスである可能性があります。それは私のポイントではありません。
- 問題1が無視されたとしても、彼がワーカー参照をどうするのか不思議に思います。
明らかに、その意図は、 (ポリモーフィック)インターフェースによってアクセスされるMyWorker
NVIで使用することです。これにより、実装がタイプの(値)メンバーを所有することが除外されます。これは、スライスなどが発生するためです。値セマンティクスはポリモーフィズムとうまく混ざりません。 そのクラスはに不明であるため、タイプのデータメンバーを持つこともできません。 したがって、元のオブジェクトとポリモーフィックな性質を維持しながら、ポインターまたは参照を介して使用することを意図していたに違いないと結論付けます。GenericTableAlgorithm
GTAClient
GTAClient
MyWorker
GenericTableAlgorithm
- 一時オブジェクト()へのポインタが
MyWorker()
良いアイデアになることはめったにないので、作者の計画は(const)参照にバインドされた一時オブジェクトの延長された寿命を使用し、そのような参照をオブジェクトpimpl_
ポイントに格納してそこから使用することだったと思います。(注:GTAClientには、これを機能させる可能性のあるクローンメンバー関数もありません。バックグラウンドにRTTI-typeinfoベースのファクトリが潜んでいるとは限りません。)
そして、ここで(ついに!)私の質問は次のようになります。(どのように)寿命が延長されたクラスの参照メンバーに一時を渡すことは合法的に行うことができますか?
§12.2.5の標準(C ++ 0xバージョンですが、C ++でも同じです。チャプター番号はわかりません)では、ライフタイム拡張から次の例外が発生します。
"-コンストラクターの参照メンバーに一時的にバインドされます。ctor-initializer(12.6.2)は、コンストラクターが終了するまで存続します。」
したがって、オブジェクトはクライアントコードa.Process()の呼び出しでは使用できません。参照された一時的なfromMyWorker()
はすでに死んでいるからです!
問題を実証する私自身のクラフトの例を考えてみましょう(GCC4.2でテスト済み):
#include <iostream>
using std::cout;
using std::endl;
struct oogie {
~oogie()
{
cout << "~oogie():" << this << ":" << m_i << endl;
}
oogie(int i_)
: m_i(i_)
{
cout << "oogie():" << this << ":" << m_i << endl;
}
void call() const
{
cout << "call(): " << this << ":" << m_i << endl;
}
int m_i;
};
oogie func(int i_=100)
{
return oogie(i_);
}
struct kangoo
{
kangoo(const oogie& o_)
: m_o(o_)
{
}
const oogie& m_o;
};
int main(int c_, char ** v_)
{
//works as intended
const oogie& ref = func(400);
//kablewy machine
kangoo s(func(1000));
cout << ref.m_i << endl;
//kangoo's referenced oogie is already gone
cout << s.m_o.m_i << endl;
//OK, ref still alive
ref.call();
//call on invalid object
s.m_o.call();
return 0;
}
出力を生成します
oogie():0x7fff5fbff780:400 oogie():0x7fff5fbff770:1000 〜oogie():0x7fff5fbff770:1000 400 1000 call():0x7fff5fbff780:400 call():0x7fff5fbff770:1000 〜oogie():0x7fff5fbff780:400
const oogie&refの場合、func()のすぐに参照にバインドされた一時的な戻り値は、その参照の存続期間が延長されている(mainの終わりまで)ので、問題ないこと
がわかります。
しかし:1000-oogieオブジェクトは、kangoo-sが構築された直後にすでに破壊されています。コードは機能しますが、ここではアンデッドオブジェクトを扱っています...
もう一度質問をします。
まず、ここで何かが足りないので、コードは正しい/合法ですか?。
次に、-Wallが指定されていても、GCCが警告を表示しないのはなぜですか?それが必要ですか?できますか?
お時間をいただきありがとうございます、
マーティン