Простая программа, использующая одинаковую инициализацию для создания объекта, не компилируется

Рассмотрим следующую программу:

struct X
{
X(int, int) { }
X(X&&) { }
};

int main()
{
X x( {0, 1} ); // Doesn't compile on ICC 13.0.1, compiles on
// Clang 3.2, GCC 4.7.2, and GCC 4.8.0 beta.
}

При компиляции с GCC 4.7.2, GCC 4.8.0 и Clang 3.2 эта программа выполняет следующие действия (*):

  1. Создает временный тип X проходящие значения 0 а также 1 затем конструктору;
  2. Move-конструкции X из этого временного.

Вместо этого ICC 13.0.1 не компилируется.

ВОПРОС № 1: Кто прав?

(*) На самом деле создание временного объекта и вызов конструктора перемещения исключаются, но компилируются с -fno-elide-constructors опция и добавление некоторых распечаток в конструкторы показывает, что это то, что происходит.


Теперь рассмотрим следующее, небольшое изменение вышеуказанной программы, где равномерная инициализация используется для прямой инициализации x:

int main()
{
X x{ {0, 1} }; // ERROR! Doesn't compile.
//     ^........^
}

Я бы не ожидал, что использование скобок вместо скобок что-то здесь изменит, но это как-то так: эта программа не компилируется любой из компиляторов, на которых я его тестировал (Clang 3.2, GCC 4.7.2, GCC 4.8.0 beta и ICC 13.0.1).

ВОПРОС № 2: Почему?

6

Решение

Это просто ошибка во всех компиляторах. §8.5.4 / 3 говорит,

Инициализация списка объекта или ссылки типа T определяется следующим образом:

— Если список инициализаторов не имеет элементов и T является типом класса с конструктором по умолчанию, объект
значение инициализации.

— В противном случае, если T является агрегатом, выполняется агрегатная инициализация (8.5.1).

— В противном случае, если T является специализацией std :: initializer_list, объект initializer_list создается, как описано ниже, и используется для инициализации объекта…

— В противном случае, если T является типом класса, учитываются конструкторы. Применимые конструкторы перечисляются, и лучший выбирается через разрешение перегрузки (13.3, 13.3.1.7). Если для преобразования какого-либо из аргументов требуется сужающее преобразование (см. Ниже), программа является некорректной.

— В противном случае, если T является ссылочным типом, prvalue, временный для типа, на который ссылается T, инициализируется списком, и ссылка привязывается к этому временному объекту.

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

Есть довольно много случаев. Обратите внимание, что вы на самом деле инициализируете список prvalue временного объекта, привязанного к rvalue ссылке.

Что касается GCC, я думаю, что он пытается применить последний элемент, упомянутый выше, для списка одноэлементного инициализатора. Если я изменю подпись конструктора на X(X&&, int = 3)затем инициализатор { {0, 1} } не удается, но { {0, 1}, 3 } преуспевает. Единственный элемент должен быть успешным, потому что, поскольку этот элемент является списком фигурных скобок инициализации, и я считаю, что регистр должен допускать дополнительные заключающие в скобки символы, аналогичные скобкам. Но неудача аналогична другим недостаткам GCC с опорой на скобки.

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

Более конкретно рассмотрев сообщения об ошибках (спасибо за ссылку LWS),

  • ICC настаивает на том, что ожидает выражения. Это неверно, потому что в соответствии с базовой грамматикой, фигурные скобки-списки инициализации могут включать в себя другие фигурные скобки-списки инициализации, а не только выражения.

  • Clang говорит msgstr «конструктор-кандидат недопустим: невозможно преобразовать аргумент списка инициализатора в ‘X'» но это работает, если преобразование явное, используя X x{ X{0, 0 } };, Это не имеет смысла. Это не конверсия, потому что у списка нет типа для конвертации. Это инициализация списка.

  • GCC говорит «нет известного преобразования для аргумента 1 из » в ‘X&&«» предполагая, что это даже не дошло до определения временного привязки к ссылке. Как и в случае с Clang, он, похоже, пытается поддельное преобразование и указывает X{0,0} исправляет это.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]