Подтвердить что ты не робот

"T const и t = C(). A;" продлить срок службы "а"?

Дается следующая сценария, которая должна интерпретироваться как код С++ 0x:

struct B { }; 
struct A { B b; }; 
int main() { 
  B const& b = A().b; 
  /* is the object still alive here? */
}

Clang и GCC (версия для стволов по состоянию на 2011/02 год) ведут себя по-другому: Clang продлевает срок службы. GCC перемещает B в новый временный объект, а затем привязывает ссылку к этому новому временному.

Я не могу найти, что любое поведение может быть получено из слов Стандарта. Выражение A().b не является временным (см. 5.2.5). Может кто-нибудь объяснить мне следующее?

  • Желаемое поведение (цель комитета)
  • Поведение, которое вы выведете из FDIS

Спасибо!

4b9b3361

Ответ 1

В пункте 12.2 параграфа N3126 = 10-0116 говорится, что:

Второй контекст [в котором временные разрушен в другой точке, чем конец полное выражение] - это когда ссылка привязан к временному. Временный к которой ссылка привязана, или временный, являющийся полным объектом подобъекта, к которому ссылка связана с сохранением времени жизни ссылка кроме...

а затем следует список из четырех специальных случаев (ctor-инициализаторы, ссылочные параметры, возвращаемое значение, новый инициализатор).

Итак (в этой версии) мне кажется, что clang является правильным, потому что вы привязываете ссылку на подобъект временного.

ИЗМЕНИТЬ

Думая на базовый под-объект объекта, это также кажется единственным разумным поведением. Альтернативой будет означать, что вырезать в:

Derived foo();
...
void bar()
{
    Base& x = foo(); // not very different from foo().b;
    ...
}

На самом деле, после небольшого эксперимента, действительно, что g++ отличает субобъект-член и базовый под-объект, но я не понимаю, где это дифференцирование выполняется в стандарте. Ниже приведена тестовая программа, которую я использовал, и там, где она отчетливо видна при обращении с двумя случаями... (B is Base, 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++, либо ошибка в том, что соответствует стандарту С++, если это действительно ожидаемое поведение или возможное приемлемое поведение (но я должен сказать, что я не очень много думал об этом, это просто ощущение, что с этой дифференциацией что-то не так).

Ответ 2

Хорошо, я делаю 180 градусов на этом

После обновления моих знаний о стандарте, я должен признать что, вероятно, справедливо ожидать, что объект, на который ссылается b, останется в живых (будет расширен) для продолжительности области действия, в которой const & был инициализирован. Я нашел GotW # 88 полезным источником для этого.

Я не вижу, как A().b структурно или семантически отличается от

string f() { return "abc"; } // ABC initializes return-value **TEMP**

void g() {
const string& s = f();  // initializes with reference to a temp
  cout << s << endl;    // '*&s' is extended per standard
}

Извините за любую путаницу, которую я мог вызвать. Я был немного из глубины.

Ответ 3

Временные объекты отличаются обстоятельствами их создания. (§12.2 "Временные классы типа класса создаются в разных контекстах..." )

Для временных, созданных ссылочным декларатором, §12.2 относится к §8.5. С++ 03 и С++ 11 сильно отличаются в разделе 8.5.5, но оба явно поддерживают ваш код.

С++ 03 говорит, что либо

- Ссылка привязана к объекту, представленному rvalue (см. 3.10), или к под-объекту внутри этого объекта.

- Создается временный тип "cv1 T2" [sic], и вызывается конструктор для копирования всего объекта rvalue во временный. Ссылка привязана к временному или к под-объекту в пределах временного.

Обсуждение полностью относится к подобъектам, не отличая базовые классы от членов. Таким образом, если привязка ссылки к элементу запрещена, то так привязывает элемент к базе, что исключает ScopeGuard.

С++ 11 более подробный, но указывает

- В противном случае ссылка должна быть ссылкой lvalue на нелетучий const-тип (то есть cv1 должен быть const), или ссылка должна быть ссылкой rvalue.... Если выражение инициализатора... представляет собой значение xvalue, класс prvalue, значение prvalue массива или значение lvalue функции и "cv1 T1" совместимый с "cv2 T2"... тогда ссылка привязана к значению выражения инициализатора. "

В сочетании с ответом 6502 и бессмысленностью привязки ссылки к значению, которое заканчивается в точку с запятой, очевидно, что С++ 11 продолжает поддерживать это поведение.

Ответ 4

Посмотрим (все ссылки на FDIS):

struct B { }; 
struct A { B b; }; 
int main() { 
  B const& b = A().b; 
}

1) 5.2.3/2 говорит, что A() является prvalue.

2) 5.2.5/4 говорит, что A().b является prvalue из-за точки 1).

3) 8.5.3/5 говорит, что B const& b привязывается непосредственно к A().b без создания временного.

4) 12.2/5 говорит, что время жизни временной привязки к ссылке расширено.

Итак, похоже, что GCC здесь не так.

Правильно ли Clang или если это UB зависит от того, является ли подобъект временного временным. Я вполне уверен, что ответ должен быть положительным, но Стандарт, похоже, молчал об этом. Если кто-то представляет DR?

EDIT: Как указано в @6502, 3.7.5 указывает, что время жизни подобъекта - это время жизни его полного объекта.