このコードを検討してください( デモ ):
#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-arrayのコピーへの宣言はconst Clangによって、non-const GCCによって宣言されます。
C-arrayのGCCの動作は、集計またはタプルのようなタイプで観察される動作と一致しています。
一方、私が標準を読んだことから、Clangは記述された内容に従っていると思います。 In [dcl.struct.bind]/1e has type cv Aここで、Aは初期化子式のタイプで、cvは構造化バインディング宣言のcv-qualifier。そして、初期化式ar
のタイプは、それに応じて [expr.type]/1const int[2]
。
何を期待すべきですか?私の意見では、Clangは標準に従っていると思います。一方、私は、配列、集約、およびタプルのようなタイプの振る舞いは同等であるという意図を感じています。
[dcl.struct.bind] での標準の表現は、次のように述べています。
initializerのassignment-expressionの配列型が
A
およびnoref-qualifierが存在し、e
にはタイプcvA
および各要素は、assignment-expressionの対応する要素からコピー初期化または直接初期化されますinitializerの形式で指定されています。
auto [i,j] = ar;
があり、ar
には配列タイプconst int[2]
があり、標準の表現ではe
がconst int[2]
型であることが明確になっています。したがって、表現ごとに、各バインディングは要素タイプ(const int
)を参照します。 Clangは技術的に正しいです。
ただし、Richard Smithが gcc bug 80649 で指摘しているように:
これは規格のバグだと思います。配列型のcv修飾子は、通常の自動控除の場合と同様に破棄する必要があります。
そうですね。 auto x = y;
を書くとき、x
がトップレベルのconst
ではないことは確かに予想できますが、ここではまだそうなります。まだコア問題が未解決だとは思いませんが、存在するはずです。