Подтвердить что ты не робот

Почему включение <utility> разбивает структурированные привязки в GCC?

Рассмотрим:

struct Point { int x, y; };

int main()
{
    const auto [x, y] = Point{};
}

Этот код компилируется с помощью gcc 7.1 в режиме С++ 17, однако этот:

#include <utility>

struct Point { int x, y; };

int main()
{
    const auto [x, y] = Point{};
}

дает ошибку:

bug.cpp: In function 'int main()':
bug.cpp:7:16: error: 'std::tuple_size<const Point>::value' is not an integral constant expression
     const auto [x, y] = Point{};
                ^~~~~~

Что здесь происходит? Ошибка компилятора, или как это должно работать структурированные привязки?

4b9b3361

Ответ 1

Это ошибка компилятора 78939. Хотя это немного сложнее, - было несколько проблем между основным языком и библиотекой, которые были взаимно противоречивыми (GB 20, LWG 2770 и LWG 2446), которые приводят к типу поведение, которое демонстрирует здесь gcc/libstdС++. Разумеется, предполагается, что код работает с или без #include <utility>, это просто вопрос о том, что стандартная формулировка попала туда должным образом.


Да, классы со всеми общественными членами, не являющимися анонимными членами, должны использоваться в объявлениях структурированных привязок для [dcl.struct.bind]/4

В противном случае все члены нестатических данных E должны быть публичными прямыми членами E или одного и того же однозначного публичного базового класса E, E не должны иметь анонимного члена объединения, а количество элементов в списке идентификаторов должно быть равно числу нестатических элементов данных E. Обозначая нестатические элементы данных E как m0, m1, m2,... (в порядке объявления), каждое vi является именем lvalue, которое относится к члену mi e и тип которого cv Ti, где Ti - объявленный тип этого члена; ссылочный тип - cv Ti. Lvalue - это бит-поле, если этот элемент является битовым полем. [Пример:

struct S { int x1 : 2; volatile double y1; };
S f();
const auto [ x, y ] = f();

Это полностью не связано с включением <utility>, ничего в этом коде не зависит от какой-либо функциональности библиотеки - элементы захватываются напрямую, а не через механизм get/tuple_size.

Ответ 2

Основная идея структурированных привязок заключается в том, что std::tuple_size<T> определяет, сколько компонентов вы получаете от распаковки T, а T::get<N> должно получить доступ к элементу N'th. Не удивительно, что этот std::tuple_size<T> является специализацией базового шаблона в <utility>.

Теперь в этом случае Point не имеет такой поддержки для структурированных привязок, но это особый случай (все публичные нестатические члены), для которых С++ 17 заявляет, что никакой специальной поддержки распаковки не требуется. Это исключение из правила выше.

Компилятор здесь отключается и пытается использовать общее правило, когда видит неспециализированный std::tuple_size из <utility>.