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

Порядок создания объектов в расширенном списке инициализации

#include <iostream>

struct A
{
    A() { std::cout << "(A::A)"; }
};

struct B
{
    B() { std::cout << "(B::B)"; }
};

struct C
{
    template<typename ...Args>
    C(Args && ...) {}
};

int main(int agrc, char *argv[])
{
    C {A(), B()}; // <-- prints (B::B)(A::A)
    std::cout << std::endl;
    C {(A(), B())}; // <-- prints (A::A)(B::B)
    std::cout << std::endl;

    return 0;
}

У меня есть 2 вопроса:

  • Почему в первом созданном объекте списка инициализации создаются в порядке справа налево?
  • Почему скобки во втором случае возвращают этот порядок?

Изменить: я скомпилировал его с msvs 2013

4b9b3361

Ответ 1

Во втором случае вы фактически инициализируетесь только с помощью B(); с помощью оператора запятой, A() был построен и выброшен первым.

C {(A(), B())};
//|^^^^^^^^^^|
//      \--------> (A(), B())
//                  ^^^  ^^^
//                   |    |
//                   /    \
//            evaluated,   THEN evaluated,
//            discarded      used

С другой стороны, в первом случае вы инициализируете C из обоих временных страниц через список инициализаторов, чьи элементы также должны оцениваться слева направо, так как это происходит, но ваш компилятор не работает в этой связи:

[C++11: 8.5.4/4]: В списке инициализаций списка с привязкой-инициализацией предложения инициализатора, включая любой результат, полученный в результате расширений пакета (14.5.3), оцениваются в порядок, в котором они появляются.. Таким образом, каждое вычисление значения и побочный эффект, связанные с данным предложением инициализатора, секвенируются перед каждым вычислением значения и побочным эффектом, связанным с любым предложением инициализатора, которое следует за ним в разделенном запятой список списка инициализаторов. [Примечание. Это упорядочение оценки выполняется независимо от семантики инициализации; например, он применяется, когда элементы списка инициализатора интерпретируются как аргументы вызова конструктора, хотя обычно нет ограничений последовательности для аргументов вызова. -end note]

Я могу воспроизвести проблему с GCC 4.8 * но Clang 3.5 ведет себя правильно & dagger;. Ошибка обсуждалась в списке std-обсуждений до & ddagger; но я еще не нашел идентификатор GCC Bugzilla еще & sect;.

C {A(), B()};
// ^^^  ^^^
//  |    \
// eval-  THEN
// uated   evaluated
//  \       /
//   \     /
//  both used

*http://coliru.stacked-crooked.com/a/1f18e0d1f8973f3c
& dagger;http://coliru.stacked-crooked.com/a/5a6e7506e9be97c3
& ddagger;https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/TQUnBFkUBDg
& sect;# 51253 могут быть связаны.

Ответ 2

Почему в первых элементах с предварительным списком инициализации создаются в порядке справа налево?

Нет. Это слева направо. У вашего компилятора есть ошибка, поэтому он оценивает право налево. Известно, что GCC (4.8) имеет эту ошибку. Вы используете GCC?

Почему скобки во втором случае возвращают этот порядок?

То же самое. Слева направо. В этом случае в картину входит запятая, которая оценивает операнды слева направо.

Ответ 3

Это старая ошибка gcc 4.8.1 (я полагаю, вы используете GCC) или другого компилятора. Я написал об этой ошибке несколько месяцев назад список инициализаторов: ошибка GCC 4.8.1

Хотя он написан на русском языке, но вы можете перевести его на английский, используя, например, перевод google service.

Как было сказано другими, элементы списка инициализаторов оцениваются слева направо и все побочные эффекты применяются перед оценкой следующего элемента.

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