Продление срока службы временного объекта по константной ссылке

С++

Я пытаюсь понять, как константные ссылки продлевают срок службы временных. Я запускаю код из фрагмента в один из ответов на вопрос В чем разница между переменной-указателем и переменной-ссылкой в ​​C++? и получил противоречивые результаты между VC11 и g++ 4.8. Я расширил фрагмент здесь:

#include <stdio.h>

struct scope_test
{
    ~scope_test() { printf("scope_test done!\n"); }
};

int main()
{
    const scope_test& test = scope_test();
    printf("in scope\n");
}

Ответчик получил результат:

in scope
scope_test done!

Я попробовал это в VC11 и получил это:

scope_test done!
in scope
scope_test done!

Я предположил, что результат VC11 был вызван отсутствием исключения копирования, поэтому я попытался посмотреть, даст ли отключение исключения копирования на g++ с fno-elide-constructors тот же результат, что и в VC11. (Я не думаю, что исключение копирования можно переключить в VC11.) Но g++ дает результат ответчика независимо от установки флага.

Стандарт С++ 11, ISO/IEC 14882:2011(E), §12.2/4 и /5 гласит:

Есть два контекста, в которых временные объекты уничтожаются не в конце полного выражения...

Второй контекст — это когда ссылка привязана к временному объекту. Временный объект, к которому привязана ссылка, или временный объект, являющийся полным объектом подобъекта, к которому привязана ссылка, сохраняется в течение всего времени существования ссылки, за исключением:

...

Имеет ли результат VC11 какое-либо отношение к копированию? Это ошибка VC11?

Ответчик заявляет:

временные объекты, назначенные константным ссылкам, живут до тех пор, пока константная ссылка не выйдет из области видимости.

Список исключений из §12.2/5 не исключает ссылки non-const. Чего мне не хватает в стандарте?

Удаление const в VC11 дает тот же результат, что и в VC11 с const. Удаление const в g++ дает error: invalid initialization of non-const reference of type ‘scope_test&’ from an rvalue of type ‘scope_test’. Почему есть разница?

ИЗМЕНИТЬ:

Я добавил конструкторы копирования и перемещения и попробовал:

#include <stdio.h>

struct scope_test
{
    scope_test() { printf("regular ctor\n"); }
    scope_test(const scope_test& src) { printf("copy ctor\n"); }
    scope_test(scope_test&& src) { printf("move ctor\n"); }
    ~scope_test() { printf("scope_test done!\n"); }
};

int main()
{
    const scope_test& test= scope_test();
    printf("in scope\n");
}

Независимо от переключения копии, g++ дает:

regular ctor
in scope
scope_test done!

VC11 дает то же самое, даже если удалить const. Если const удалить из g++, g++ все равно даст error: invalid initialization of non-const reference of type ‘scope_test&’ from an rvalue of type ‘scope_test’.


person CodeBricks    schedule 10.10.2013    source источник
comment
Однако никакого копирования нет, поскольку существует только один объект.   -  person Kerrek SB    schedule 11.10.2013
comment
const не имеет ничего общего с продлением срока службы. Подойдет любая ссылка. Просто неконстантные ссылки lvalue не могут быть привязаны к rvalue, поэтому их никогда нельзя использовать таким образом.   -  person Kerrek SB    schedule 11.10.2013
comment
@KerrekSB: привязка временной ссылки к const может привести к созданию копии. Самый простой способ проверить, была ли создана копия, — настроить конструктор копирования.   -  person Dietmar Kühl    schedule 11.10.2013
comment
@DietmarKühl: это не функция, возвращающая значение, это буквально объект в RHS.   -  person Kerrek SB    schedule 11.10.2013
comment
Если есть общедоступный ответ на этот вопрос, он находится здесь: blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx Я не проходил через это, но, зная, как медленно Microsoft внедряет новый стандарт C++, возможно, это просто так. это ВК.   -  person tomi.lee.jones    schedule 11.10.2013
comment
@KerrekSB: Да, но слова не предписывают привязку к одному и тому же объекту только к объекту с тем же значением, то есть потенциально к копии 8.5.3 [dcl.init.ref], параграф 5: Если выражение инициализатора .. .. является значением x... тогда ссылка привязывается к значению выражения инициализатора.... C++03 более явно разрешает копирование: если выражение инициализатора является значением r... ссылка привязывается к одному из следующими способами (выбор определяется реализацией): ... и вызывается конструктор для копирования всего объекта rvalue во временное. (опущено из-за ограниченности места).   -  person Dietmar Kühl    schedule 11.10.2013
comment
@DietmarKühl: очень интересно. Звучит как копирайон...   -  person Kerrek SB    schedule 11.10.2013
comment
@KerrekSB: re: The const has nothing to do with the lifetime prolongation...It's just that non-const lvalue references can't bind to rvalues, so the can never be used that way. Согласно ответу DietmarKühl: Otherwise, the reference shall be to a non-volatile const type..., в формулировке Стандарта не говорится, что ссылка должна быть объявлена ​​константной; в нем указано, что референт должен быть константным типом. Неконстантная форма компилируется в VC11. Я что-то упустил здесь?   -  person CodeBricks    schedule 12.10.2013
comment
@CodeBricks: ссылка может быть T&&, и это продлит срок службы.   -  person Kerrek SB    schedule 12.10.2013
comment
@CodeBricks: Объяснения Дитмара: я думаю, что основной интерес в возможности делать копии заключается в том, что вы говорите что-то вроде const T & x = foo() с T foo();. В этом случае вы определенно хотите иметь возможность копировать результат вызова функции в локальную область. Но когда у вас есть T const & x = T(), объект, возможно, уже находится в локальной области видимости, поэтому я надеюсь, что копирование не требуется, хотя стандарт, похоже, все равно разрешает это (возможно, чтобы просто не создавать дополнительных правил о различиях). Просто мое мнение...   -  person Kerrek SB    schedule 12.10.2013
comment
@KerrekSB: Значит, scope_test& test= scope_test(); неправильно сформирован и, следовательно, является ошибкой VC11, поскольку он компилируется в VC11, но не в g++? В этом отношении scope_test test = *const_cast<const scope_test* const>( &( (scope_test()) ) ); также компилируется в VC11, но не в g++; g++ указывает error: taking address of temporary   -  person CodeBricks    schedule 12.10.2013


Ответы (1)


Оба поведения правильны, безусловно, в соответствии со стандартом С++ 03 (8.5.3 [dcl.init.ref], параграф 5):

В противном случае ссылка должна быть на неизменяемый константный тип (т. е. cv1 должен быть константным). [Пример: ...]

  • Если выражением инициализатора является rvalue с T2 типом класса, а «cv1 T1» совместим по ссылке с «cv2 T2», ссылка связывается одним из следующих способов (выбор определяется реализацией):

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

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

Я думаю, что определение С++ 11 по-прежнему позволяет делать копии, но формулировка не так явно разрешает копирование. В любом случае, VC++ не претендует на полную совместимость с C++11.

person Dietmar Kühl    schedule 10.10.2013
comment
Оба поведения верны, вы имеете в виду, что выходные данные VC11 и g++ верны для примера с константной ссылкой или для примера с неконстантной ссылкой? Кроме того, я добавил элементы копирования и перемещения (см. РЕДАКТИРОВАТЬ). Если пример VC11 является случаем — A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary...; означает ли это, что VC11 здесь отсутствует, поскольку он не выводит copy ctor или move ctor, но не тогда, когда я не определяю свою собственную копию и не перемещаю ctors? - person CodeBricks; 11.10.2013