Почему включение ‹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{};
                ^~~~~~

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


person robson3.14    schedule 10.05.2017    source источник
comment
Я только что проверил, и clang 4.0.0 ведет себя точно так же.   -  person HolyBlackCat    schedule 10.05.2017
comment
Похоже, на недавнем собрании Kona (27 февраля 2017 г. - 04 марта 2017 г.) в этой области произошли изменения. Эта функция еще не разработана.   -  person MSalters    schedule 10.05.2017


Ответы (2)


Это ошибка компилятора 78939. Хотя это немного сложнее - было несколько проблем между основным языком и библиотекой, которые противоречили друг другу (GB 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. lvalue является битовым полем, если этот элемент является битовым полем. [ Пример:

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

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

person Barry    schedule 10.05.2017
comment
@MSalters Чувак, поиск gcc bugzilla отстой. Я искал несколько минут и ничего не нашел... - person Barry; 10.05.2017
comment
Это стандартная ошибка, вызванная тем, что CWG и LWG не синхронизированы, а не совсем ошибка компилятора (она буквально реализует спецификацию). Первоначально шаг 2 структурированных привязок проверял tuple_size<T>::value, но tuple_size<cv T> не был дружественным к SFINAE. Поэтому LWG изменила tuple_size<cv T> на пустое, если tuple_size<T>::value не работает. Затем GB 20 говорит, что проверка tuple_size<T>::value слишком враждебна пользователю, поэтому теперь ядро ​​​​просто проверяет, завершено ли tuple_size, и, если это так, выполняет механизм, подобный кортежу. Конечно, это нанесло ущерб библиотечному исправлению tuple_size<cv T>. - person T.C.; 10.05.2017
comment
@Т.С. Знаете ли вы, есть ли план исправить это? Есть ли шанс исправить это в C++ 17, или у нас будет исправление сразу после его публикации? - person Ilya Popov; 12.05.2017
comment
@Т.С. Спасибо за редактирование. Странно, что старая ссылка все еще работает, какое-то время меня это смущало. Просто артефакт? Сама формулировка мне кажется одинаковой. - person Barry; 12.05.2017
comment
@Barry Может быть, они просто скопировали новые файлы, не очищая старые при обновлении. Я НЕ ЗНАЮ. - person T.C.; 12.05.2017

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

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

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

person MSalters    schedule 10.05.2017