Некоторое время назад можно было использовать «назначенный инициализатор» в GCC:
struct CC{
double a_;
double b_;
};
CC cc{.a_ = 1., .b_ = 2.}; assert(cc.a_ == 1. and cc.b_ == 2.); // ok
CC cc{.bla = 0., .bli = 0.}; // compile error
Однако, когда я добавляю конструктор, метки игнорируются.
struct CC{
double a_;
double b_;
CC(double a, double b) : a_{a}, b_{b}{}
};
CC cc{.a_ = 1., .b_ = 2.}; assert(cc.a_ == 1. and cc.b_ == 2.); // ok
CC cc{.b_ = 2., .a_ = 1.}; // compiles but labels don't matter only the order, confusing
CC cc{.bla = 2., .bli = 1.}; // compiles but labels don't matter, confusing
Другими словами, синтаксис инициализатора с конструктором заставляет метку вести себя просто как комментарий !, что может быть очень запутанным, но, прежде всего, это очень странно.
Я обнаружил это случайно, с gcc 8.1 -std=c++2a
,
Это ожидаемое поведение?
Ссылка: https://en.cppreference.com/w/cpp/language/aggregate_initialization
Это ошибка GCC, это все еще собирает даже с -педантиком в котором мы должны получать предупреждения для любых расширений
…чтобы получить всю диагностику, требуемую стандартом, вы должны также указать -педантичный …
и GCC утверждает, что поддерживает P0329R4: Designated initializers
предложение для C++2a
Режим в соответствии с поддержкой стандартов C ++ на странице GCC:
Особенность языка | Предложение | Доступно в GCC?
…
Назначенные инициализаторы | P0329R4 | 8
Для того, чтобы использовать Назначенные инициализаторы тип должен быть агрегированным [Dcl.init.list] P3.1:
Если braced-init-list содержит назначенный-initializer-list, T должен быть совокупным классом. Заказанный
идентификаторы в обозначениях обозначенного-инициализатора-списка образуют подпоследовательность упорядоченного
идентификаторы в непосредственных нестатических членах данных T. Агрегированная инициализация выполняется (11.6.1).
[ Пример:struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // error: designator order does not match declaration order A b{.x = 1, .z = 2}; // OK, b.y initialized to 0
— конец примера]
CC
не является совокупностью в соответствии с [Dcl.init.aggr]:
Агрегат — это массив или класс (раздел 12) с
— (1.1) — нет пользовательских, явных или унаследованных конструкторов (15.1),
….
Если мы посмотрим на Отчет об ошибке gcc: неверное разрешение перегрузки при использовании назначенных инициализаторов мы видим в этом примере:
Другой контрольный пример, уменьшенный с Chromium 70.0.3538.9 и принятый
лязг:struct S { void *a; int b; }; void f(S); void g() { f({.b = 1}); }
Это не с
bug.cc: In function ‘void g()’: bug.cc:3:24: error: could not convert ‘{1}’ from ‘<brace-enclosed initializer list>’ to ‘S’ void g() { f({.b = 1}); } ^
Ошибка предполагает, что имена полей просто полностью игнорируются во время
разрешение перегрузки, которое также объясняет поведение
первоначально сообщенный код.
Кажется, gcc игнорирует имена полей во время разрешения перегрузки. Что объясняет странное поведение, которое вы видите, и то, что мы получаем правильную диагностику, когда удаляем конструктор.
Метки для обозначенных инициализаторов не должны игнорироваться компиляторами. Все три примера с конструктором должны быть некорректными.
Вы, вероятно, получаете такое поведение из-за конфликта между назначенной функцией инициализатора C ++ 20 и инициализированными инициализаторами в стиле C GCC, к которым вы неявно обращаетесь из-за того, что GCC просто предоставляет их вам. Если бы GCC был должным образом совместим с C ++ 20, после того, как вы дали типу конструктор, он перестал бы быть агрегатом, и, следовательно, использование инициализатора было бы некорректным.
По сути, это ошибка драйвера, вызванная не-C ++ — стандартным поведением, которое компилятор предоставляет вам по умолчанию. Скорее всего, если вы отключите эту функцию, вы получите правильную ошибку компиляции для этих случаев.