Должна ли const иметь структурированную привязку к копии const c-массива?

Рассмотрим этот код (демо):

#include <tuple>
#include <type_traits>

struct Ag{int i;int j;};
using  T = std::tuple<int,int>;
using  Ar = int[2];

const Ag ag {};
const T t   {};
const Ar ar {};

void bind_ag(){
    auto [i,j] = ag;
    static_assert(std::is_same_v<decltype((i)),int&>);
    }
void bind_t(){
    auto [i,j] = t;
    static_assert(std::is_same_v<decltype((i)),int&>);
    }
void bind_ar(){
    auto [i,j] = ar;
    static_assert(std::is_same_v<decltype((i)),int&>);       //For GCC
    static_assert(std::is_same_v<decltype((i)),const int&>); //For Clang (and standard?)
    }

Структурированная привязка к копии const c-массива объявляется const Clang и неконстантной GCC.

Поведение GCC для c-массива согласуется с поведением, наблюдаемым для агрегатных или кортежоподобных типов.

С другой стороны, из моего прочтения стандарта я полагаю, что Clang следует тому, что написано. В [dcl.struct.bind]/1 e имеет тип cv A, где A - тип выражения инициализатора, а cv - cv-квалификатор объявления структурированной привязки. И тип выражения инициализатора ar соответствует [expr.type]/1 const int[2].

Чего следует ожидать? Мое мнение таково, что Clang следует стандарту. С другой стороны, я чувствую, что намерения в том, чтобы поведение для массивов, агрегатов и кортежоподобных типов было эквивалентным.

Ответ 1

На мой взгляд, GCC является правильным (и, как вы указали, более интуитивным), хотя, безусловно, некоторые люди не согласны.

У decl-specier-seq (" auto ") нет cv-квалификаторов (т.е. Вы не написали const auto), и это должно быть то, что считается cv, когда вы решаете, какие квалификаторы ставить в привязки.

[dcl.struct.bind]/1: [..] Пусть cv обозначает cv-квалификаторы в decl-specier-seq. [..]

[dcl.struct.bind]/3: Если E является типом массива с типом элемента T, количество элементов в списке идентификаторов должно быть равно количеству элементов в E Каждый v i является именем lvalue, которое ссылается на элемент i массива и тип которого T; ссылочный тип - T [Примечание: cv-квалификаторы верхнего уровня для T - cv. - конец примечания] [..]

Как и вы, разработчики Clang интерпретируют здесь auto как имеющий cv-квалификацию const, потому что тип, который он выводит, это const Ag. Но я не вижу стандартного оправдания такой интерпретации.

Ответ 2

Формулировка стандарта в [dcl.struct.bind] гласит:

Если выражение присваивания в инициализаторе имеет тип массива A а квалификатор ref отсутствует, e имеет тип cv A и каждый элемент инициализируется копией или инициализируется напрямую из соответствующего элемента выражения присваивания, как указано в форме инициализатора.

У нас есть auto [i,j] = ar; , ar имеет тип массива const int[2], и формулировка стандарта проясняет, что e имеет тип const int[2]. Таким образом, в соответствии с формулировкой, каждая привязка ссылается на тип элемента - который является const int. Лягушка технически правильна.

Однако, как указывает Ричард Смит в сообщении об ошибке gcc 80649:

Я думаю, что это ошибка в стандарте. Спецификаторы cv типа массива должны быть отброшены, как и для любого обычного автоматического вывода.

Это кажется правильным. Когда вы пишете auto x = y; вы, конечно, ожидаете, что x не будет const верхнего уровня, но здесь у нас есть ситуация, когда он все еще существует. Я не думаю, что есть основной вопрос, открытый для этого, но он должен быть.