Кто здесь находится в `std :: initializer_list`? clang ++ или g ++? [Дубликат]

Когда вы конвертируете .1 или 1/10 в base 2 (двоичный), вы получаете повторяющийся шаблон после десятичной точки, точно так же, как пытаетесь представить 1/3 в базе 10. Значение не является точным, и поэтому вы можете 't делать точную математику с ней, используя обычные методы с плавающей запятой.

17
задан gedamial 8 February 2016 в 16:18
поделиться

2 ответа

Как отметил Никол Болас, исходная версия этого ответа была неправильной: cppreference на момент написания неправильно документировал порядок, в котором конструкторы рассматривались в инициализации списка. Ниже приведен ответ с использованием правил, поскольку они существуют в черновом проекте стандарта n4140, который очень близок к официальному стандарту C ++ 14.

Текст оригинала ответ все еще включен для записи.


Обновленный ответ

В комментарии к NathanOliver, gcc и clang производят разные выходы в этой ситуации:

g++ -std=c++14 -Wall -pedantic -pthread main.cpp && ./a.out
default ctor
copy ctor
copy ctor
initializer list


clang++ -std=c++14 -Wall -pedantic -pthread main.cpp && ./a.out
default ctor
copy ctor
copy ctor

gcc верен.

n4140 [dcl.init.list] / 1

Инициализация списка - это инициализация объекта или ссылки из скопированного списка init.

< / blockquote>

Вы используете там инициализацию списка, а поскольку c является объектом, правила его инициализации списка определены в [dcl.init.list] / 3:

[dcl.init.list] / 3:

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

  1. Если T является агрегатом ...
  2. В противном случае, если в списке инициализаторов нет элементов ...
  3. В противном случае, если T является специализацией std::initializer_list<E> ...

, проходящий через список:

  1. Foo не является агрегатом.
  2. Он имеет один элемент .
  3. Foo не является специализацией std::initializer_list<E>.

Затем мы нажимаем [dcl.init.list] /3.4:

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

Теперь мы куда-то попадаем. 13.3.1.7 также известен как [over.match.list]:

Инициализация с помощью инициализации списка Когда объекты неагрегатного типа класса T инициализируются по списку (8.5.4) , разрешение перегрузки выбирает конструктор в две фазы:

  1. Первоначально функции-кандидаты являются конструкторами-списками инициализаторов (8.5.4) класса T, а список аргументов состоит из инициализатора список как один аргумент.
  2. Если не найдено никакого жизнеспособного конструктора-списка инициализатора, разрешение перегрузки выполняется снова, где кандидатные функции являются всеми конструкторами класса T, а список аргументов состоит из элементов списка инициализаторов.

Таким образом, конструктор копирования будет считаться только после конструкторов списка инициализаторов во второй фазе разрешения перегрузки. Здесь должен использоваться конструктор списка инициализаторов.

Стоит отметить, что [over.match.list] затем продолжается:

Если в списке инициализаторов нет элементов и T имеет конструктор по умолчанию, первая фаза опущена. В инициализации списка копий, если выбран явный конструктор, инициализация плохо сформирована.

и что после [dcl.init.list] /3.5 имеет дело с одноэлементной инициализацией списка :

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

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


Оригинальный ответ

Вы сталкиваетесь с интересным аспектом инициализации списка, где, если список выполняется некоторые требования к нему могут рассматриваться как инициализация копирования, а не инициализация списка.

из cppreference :

Эффекты инициализации списка объекта типа T являются:

Если T - тип класса, а в списке инициализаций имеется один элемент того же или производного типа (возможно, cv-qualified), объект инициализируется из этого элемента (путем инициализации копирования для инициализации списка копий или путем прямой инициализации для инициализации прямого списка). (поскольку c ++ 14)

Foo c{b} выполняет все эти требования.

18
ответ дан jaggedSpire 4 September 2018 в 08:49
поделиться

Давайте рассмотрим, что здесь говорит спецификация C ++ 14 о инициализации списка. [dcl.init.list] 3 имеет последовательность правил, которые должны применяться по порядку:

3.1 не применяется, поскольку Foo не является совокупностью.

3.2 не применяется, поскольку список не пуст.

3.3 не применяется, поскольку Foo не является специализацией initializer_list.

3.4 действительно применяется, поскольку Foo - тип класса. В нем говорится о конструкторах с разрешением перегрузки в соответствии с [over.match.list]. И это правило говорит, что сначала проверить initializer_list конструкторы . Поскольку ваш тип имеет конструктор initilaizer_list, компилятор должен проверить , чтобы узнать, может ли быть изготовлен initializer_list, соответствующий одному из этих конструкторов, из данных значений. Он может, поэтому это то, что должно называться .

Короче говоря, GCC прав, а Clang ошибочен .

Следует отметить, что рабочий проект C ++ 17 изменяет ничего об этом. Он имеет новый раздел 3.1, который имеет специальную формулировку для списков с одним значением, но применяется только к агрегатам . Foo не является совокупностью, поэтому он не применяется.

6
ответ дан Nicol Bolas 4 September 2018 в 08:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: