Должна ли 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
верхнего уровня, но здесь у нас есть ситуация, когда он все еще существует. Я не думаю, что есть основной вопрос, открытый для этого, но он должен быть.