Инициализация скобки класса неверно интерпретируется для std :: initializer_list вместо конструкции копирования - PullRequest
2 голосов
/ 06 апреля 2019

Рассмотрим следующий код C ++ 11:

#include <initializer_list>

struct MyStruct {
    MyStruct() {}
    MyStruct(const MyStruct& other) {}
    void doStuff() const {}
};

int main() {
    MyStruct a;
    auto b{a};

    a.doStuff();
    b.doStuff();
    return 0;
}

Я ожидал, что b будет экземпляром MyStruct, созданным с учетом копирования из a, но вместо этого b будет std::initializer<MyStruct> при компиляции с GCC 4.9.1. GCC 8.2 компилирует это как ожидалось.

ПРИМЕЧАНИЕ Я сделал пример этого на Годболте: https://godbolt.org/z/adNDoO

Не могли бы вы объяснить разницу между двумя версиями компилятора? (Или что стандарт говорит по этому поводу?)

Ответы [ 2 ]

1 голос
/ 06 апреля 2019

Это ошибка в C ++ 11, исправленная в C ++ 14.Похоже, что GCC 8.2 учитывает новые правила для braced-init-list (N3922) , даже если вы компилируете с флагом C ++ 11.

Новые роли говорят:

Для прямой инициализации списка:

  • Для фигурного списка инициализации только с одним элементом автоматическое удержание будет выводиться из этой записи;
  • Для списка фигурных скобок с более чем одним элементом автоматическое удержание будет некорректным.

В вашем случае:

MyStruct a;
auto b{a};

Это следует за первымПравило, поэтому он компилируется без проблем.

Старшее поколение GCC 4.9.1 не реализует эти новые правила, поэтому по умолчанию оно считает его std::initializer_list.

0 голосов
/ 06 апреля 2019

Это ожидаемое поведение, поскольку в C ++ 11:

Объект std :: initializer_list создается автоматически, когда: ... список фигурных скобок init-list привязан к auto, в том числе вранжированный для цикла

Поэтому, когда вы присваиваете {x1, x2, ..., xn} 'auto', вы получаете объект типа std::initializer_list<decltype(x1)>, если все значения имеют одинаковый тип (я пропускаю здесь ссылки и cvдля простоты):

auto a = {5};   // std::initializer_list<int>
auto b {5};     // std::initializer_list<int>
auto c = {1, 2}; // std::initializer_list<int>
auto d {1, 2};   // std::initializer_list<int>

Однако в C ++ 17 это было изменено.Были добавлены следующие правила:

  • для автоматического вывода списка инициализации будет выведен std :: initializer_list, если все элементы в списке имеют одинаковый тип, или будут неправильно сформированы.
  • для прямой инициализации списка автоматическое удержание выводит T, если список имеет один элемент, или будет некорректно сформировано, если имеется более одного элемента.

Так что, когда вы компилируете с C ++ 17соответствующий компилятор вы получите

auto a = {42};   // std::initializer_list<int>
auto b {42};     // int
auto c = {1, 2}; // std::initializer_list<int>
auto d {1, 2};   // error, too many

Я считаю, что gcc 8.2 не обрабатывает -std = c ++ 11 для этой ситуации должным образом

...