Рассматривать:
struct Point { int x, y; };
int main()
{
const auto [x, y] = Point{};
}
Этот код прекрасно компилируется с gcc 7.1 в режиме C ++ 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{};
^~~~~~
Что тут происходит? Ошибка компилятора, или это то, как должны работать структурированные привязки?
Это ошибка компилятора 78939. Хотя это немного сложнее — между базовым языком и библиотекой было несколько противоречивых проблем (ГБ 20, LWG 2770, а также LWG 2446), что приводит к тому типу поведения, который gcc / libstdc ++ демонстрирует здесь. Это конечно предназначена что код работает с или без #include <utility>
это просто вопрос стандартной формулировки, полученной там правильно.
Да, классы со всеми публичными неанонимными членами объединения должны использоваться в декларациях структурированных привязок согласно [Dcl.struct.bind] / 4:
В противном случае все
E
Нестатические данные должны быть прямымиE
или из того же однозначного общественного базового классаE
,E
не должен иметь анонимного члена объединения, а количество элементов в списке идентификаторов должно быть равно количеству элементов не статических данныхE
, Назначение нестатических данных членовE
как m0, m1, m2, … (в порядке объявления), каждый vi является именем lvalue, которое относится к члену mi элемента e и типом которого является cv Ti, где Ti является объявленным типом этого члена; ссылочный тип cv Ti. Значение l является битовым полем, если этот элемент является битовым полем. [ Пример:struct S { int x1 : 2; volatile double y1; }; S f(); const auto [ x, y ] = f();
Это совершенно не связано с включением <utility>
ничто в этом коде не зависит от какой-либо функциональности библиотеки — члены захватываются напрямую, а не через get
/tuple_size
механизм.
Основная идея структурированных привязок заключается в том, что std::tuple_size<T>
определяет, сколько компонентов вы получаете от распаковки T
, а также T::get<N>
должен получить доступ к N-му элементу. Не удивительно, это std::tuple_size<T>
это специализация из базового шаблона в <utility>
,
Теперь в этом случае Point
не имеет такой поддержки структурированных привязок, но это особый случай (все открытые нестатические члены), для которого C ++ 17 заявляет, что никакой специальной поддержки распаковки не требуется. Это исключение из правила выше.
Компилятор отключается сам по себе и пытается использовать общее правило, когда видит несвойственное std::tuple_size
от <utility>
,