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

Когда точно инициализатор временно уничтожен?

Я построил этот эксперимент сегодня, после ответа на какой-то вопрос

struct A { 
  bool &b; 
  A(bool &b):b(b) { } 
  ~A() { std::cout << b; }  
  bool yield() { return true; } 
}; 

bool b = A(b).yield();

int main() { }

b имеет значение false (в результате инициализации нуля) перед установкой его в true с помощью динамической инициализации. Если временная информация будет уничтожена до завершения инициализации b, мы напечатаем false, иначе true.

Спектр говорит, что временное уничтожается в конце полного выражения. Это не похоже на упорядочение с инициализацией b. Поэтому мне интересно

  • Позволяет ли спецификация разрешить реализации печатать как false, так и true в разных прогонах?

Clang печатает false для вышеуказанного, а GCC печатает true. Это меня смущает. Пропустил ли я некоторый текст спецификации, определяющий порядок?

4b9b3361

Ответ 1

Я думаю, что это позволяло печатать истинные, или ложные, или по нескольким не связанным причинам, ничего.

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

Ничто не возможно, потому что инициализация b не упорядочена относительно создания/инициализации std::cout; когда вы пытаетесь уничтожить временные, cout, возможно, еще не были созданы/инициализированы, поэтому попытка распечатать что-то может не работать в этой точке вообще. [Изменить: это относится к С++ 98/03 и не относится к С++ 11.]

Изменить: вот как я, по крайней мере, вижу последовательность:

enter image description here

Edit2: После перечитывания §12.2/4 (опять же), я снова изменил диаграмму. В §12.2/4 говорится:

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

Я считаю, что это выражение является инициализатором для декларатора, определяющего объект, поэтому в этом случае требуется инициализировать объект из копии значения выражения (true), а не непосредственно из возвращаемого значения. В случае true это, вероятно, является различием без разницы, но я думаю, что диаграмма технически более точна, поскольку она стоит прямо сейчас.

Это также довольно ясно (я думаю), что временное удерживание true не должно быть уничтожено в конце полного выражения, поэтому я снова нарисовал диаграмму, чтобы отразить это.

Этот раздел отсутствует в С++ 0x/С++ 11, поэтому я снова нарисовал диаграмму (еще раз), чтобы показать разницу между двумя (и насколько проще эта часть появилась в C + +11).

Ответ 2

(Цитирование стандарта С++ 03)

Сначала найдутся §12.2/3:

Когда реализация вводит временный объект класса, который имеет нетривиальный конструктор (12.1), он должен гарантировать, что конструктор вызывается для временного объекта. Аналогичным образом деструктор должен быть вызван для временного с нетривиальным деструктором (12.4). Временные объекты уничтожаются как последний шаг при оценке полного выражения (1.9), который (лексически) содержит точку, в которой они были созданы. Это справедливо, даже если эта оценка заканчивается тем, что бросает исключение.

Я считаю, что это красная селедка, из-за §1.9/13:

[Примечание: некоторые контексты в С++ вызывают оценку полного выражения, которое получается из синтаксической конструкции, отличной от выражения (5.18). Например, в 8.5 один синтаксис для инициализатора

    ( expression-list )

но результирующая конструкция представляет собой вызов функции функции-конструктора с списком выражений в виде списка аргументов; такой вызов функции является полным выражением. Например, в 8.5, еще один синтаксис для инициализатора -

    = initializer-clause

но снова результирующая конструкция может быть вызовом функции для функции-конструктора с одним присваиванием-выражением в качестве аргумента; снова вызов функции является полным выражением. ]

Это означает, что A(b).yield() само является полным выражением, не делая здесь никакого смысла в §12.2/3.

Тогда мы попадаем в точки последовательности - §1.9/7:

Доступ к объекту, обозначенному изменчивым значением lvalue (3.10), модификацией объекта, вызовом функции ввода-вывода библиотеки или вызовом функции, которая делает любую из этих операций, являются всеми побочными эффектами, которые являются изменениями состояния среда выполнения. Оценка выражения может привести к побочным эффектам. В определенных определенных точках последовательности выполнения, называемых точками последовательности, все побочные эффекты предыдущих оценок должны быть полными, и никаких побочных эффектов последующих оценок не должно быть.

§1.9/16:

Существует точка последовательности при завершении оценки каждого полного выражения.

и §1.9/17:

При вызове функции (независимо от того, является ли функция встроенной), после оценки всех аргументов функции (если они есть) есть точка последовательности, которая выполняется перед выполнением любых выражений или операторов в теле функции. Существует также точка последовательности после копирования возвращаемого значения и перед выполнением любых выражений вне функции.

Полагая все это вместе, я думаю, что Clang прав, и GCC (и MSVC 2010 SP1) ошибочен - временное, которое содержит результат выражения (срок его жизни которого расширяется согласно §12.2/4) - это bool возвращается из A::yield(), а не временный A, на который вызывается yield. Принимая во внимание §1.9, после вызова A::yield() должна быть точка последовательности, в течение которой временная A будет уничтожена.

Ответ 3

Во-первых, просто чтобы очистить абзац, который был здесь ранее, использование b в его собственной (динамической) инициализации здесь не UB. Прежде чем выражение будет оценено, b не инициализируется, но инициализируется нулем.


Временная A должна жить точно столько же, сколько полное выражение:

Временные объекты уничтожаются как последний шаг в оценке полное выражение (1.9), которое (лексически) содержит точку, в которой они были создан.

[ISO/IEC 14882: 2003 (R) 12.2/3]

Строка bool b = A(b).yield(); - это объявление, которое является выражением, которое не является выражением. Выражение под рукой обнаруживается только для RHS =. [ISO/IEC 14882: 2003 (E) A.6]

Это означало бы, что временное должно быть уничтожено до того, как произойдет динамическая инициализация, нет? Конечно, значение true сохраняется во временном, которое содержит результат выражения 1 до тех пор, пока инициализация не завершится, но исходный A временный должен быть уничтожен до того, как b будет фактически изменен.

Поэтому я ожидал бы выход false каждый раз.


1

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

[ISO/IEC 14882: 2003 (R) 12.2/4]