Конструктор мешает переменной, назначенной инициализатору?

Некоторое время назад можно было использовать "назначенный инициализатор" в 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

2 ответа

Решение

Это ошибка gcc, она по-прежнему собирается даже с -pedantic, в которой мы должны получать предупреждения для любых расширений

... чтобы получить всю диагностику, требуемую стандартом, вы также должны указать -pedantic...

и 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

Если мы посмотрим на отчет об ошибках gcc: неправильное разрешение перегрузки при использовании назначенных инициализаторов мы видим в данном примере:

Еще один тестовый пример, уменьшенный с Chromium 70.0.3538.9 и принятый clang:

  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++- стандартным поведением, которое компилятор предоставляет вам по умолчанию. Скорее всего, если вы отключите эту функцию, вы получите правильную ошибку компиляции для этих случаев.

Другие вопросы по тегам