N3126=10-0116 の 12.2 パラグラフ 5 では、次のように述べられています。
2 番目のコンテキスト [完全な式の末尾とは異なる時点で一時変数が破棄される] は、参照が一時変数にバインドされている場合です。参照がバインドされている一時オブジェクト、または参照がバインドされているサブオブジェクトの完全なオブジェクトである一時オブジェクトは、... を除き、参照の存続期間中存続します。
その後、4 つの特殊なケース (ctor-initializers、参照パラメーター、戻り値、新しい初期化子) のリストに従います。
したがって、(このバージョンでは) 参照を一時オブジェクトのサブオブジェクトにバインドしているため、clang は正しいように思えます。
編集
オブジェクトの基本サブオブジェクトを考えると、これも唯一の合理的な動作のようです。別の方法は、次のようにスライスすることを意味します。
Derived foo();
...
void bar()
{
Base& x = foo(); // not very different from foo().b;
...
}
実際に少し実験を行った後、g ++ はメンバー サブオブジェクトとベース サブオブジェクトを区別しているように見えますが、この区別が標準のどこで行われているかはわかりません。以下は、私が使用したテスト プログラムであり、2 つのケースの処理の違いが明確にわかります... (B
はベース、D
派生、C
構成)。
#include <iostream>
struct B
{
B()
{ std::cout << "B{" << this << "}::B()\n"; }
B(const B& x)
{ std::cout << "B{" << this << "}::B(const B& " << &x << ")\n"; }
virtual ~B()
{ std::cout << "B{" << this << "}::~B()\n"; }
virtual void doit() const
{ std::cout << "B{" << this << "}::doit()\n"; }
};
struct D : B
{
D()
{ std::cout << "D{" << this << "}::D()\n"; }
D(const D& x)
{ std::cout << "D{" << this << "}::D(const D& " << &x << ")\n"; }
virtual ~D()
{ std::cout << "D{" << this << "}::~D()\n"; }
virtual void doit() const
{ std::cout << "D{" << this << "}::doit()\n"; }
};
struct C
{
B b;
C()
{ std::cout << "C{" << this << "}::C()\n"; }
C(const C& x)
{ std::cout << "C{" << this << "}::C(const C& " << &x << ")\n"; }
~C()
{ std::cout << "C{" << this << "}::~C()\n"; }
};
D foo()
{
return D();
}
void bar()
{
std::cout << "Before calling foo()\n";
const B& b = foo();
std::cout << "After calling foo()\n";
b.doit();
std::cout << "After calling b.doit()\n";
const B& b2 = C().b;
std::cout << "After binding to .b\n";
b2.doit();
std::cout << "After calling b2.doit()\n";
}
int main()
{
std::cout << "Before calling bar()\n";
bar();
std::cout << "After calling bar()\n";
return 0;
}
g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 で得られる出力は
Before calling bar()
Before calling foo()
B{0xbf9f86ec}::B()
D{0xbf9f86ec}::D()
After calling foo()
D{0xbf9f86ec}::doit()
After calling b.doit()
B{0xbf9f86e8}::B()
C{0xbf9f86e8}::C()
B{0xbf9f86e4}::B(const B& 0xbf9f86e8)
C{0xbf9f86e8}::~C()
B{0xbf9f86e8}::~B()
After binding to .b
B{0xbf9f86e4}::doit()
After calling b2.doit()
B{0xbf9f86e4}::~B()
D{0xbf9f86ec}::~D()
B{0xbf9f86ec}::~B()
After calling bar()
私の意見では、これは g++ のバグか、これが実際に期待される動作または許容可能な動作である場合、c++ 標準で義務付けられているバグのいずれかです (しかし、私はそれについてあまり考えていなかったと言わなければなりません。これはこの差別化に何か問題があると感じただけです)。