26

Consider this code (demo):

#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?)
    }

A structured binding to a copy of a const c-array are declared const by Clang and non-const by GCC.

The behavior of GCC for c-array is consistent with the behavior observed for aggregate or tuple-like types.

On the other hand from my reading of the standard, I suppose Clang follows what is written. In [dcl.struct.bind]/1 e has type cv A where A is the type of the initializer expression and the cv is the cv-qualifier of the structured binding declaration. And the type of the initializer expression ar is accordingly to [expr.type]/1 const int[2].

What should be expected? My opinion is that Clang follows the standard. On the other hand I feel the intent was that the behaviors for array, aggregate and tuple-like types were equivalent.

Oliv
  • 17,610
  • 1
  • 29
  • 72

1 Answers1

20

The wording of the standard in [dcl.struct.bind] says:

If the assignment-expression in the initializer has array type A and no ref-qualifier is present, e has type cv A and each element is copy-initialized or direct-initialized from the corresponding element of the assignment-expression as specified by the form of the initializer.

We have auto [i,j] = ar;, ar has array type const int[2], and the wording of the standard makes it clear that e has type const int[2]. Thus, per the wording, each binding references the element type - which is const int. Clang is technically correct.

However, as Richard Smith points out in gcc bug 80649:

I think this is a bug in the standard. The cv-qualifiers of the array type should be discarded, as they would be for any normal auto deduction.

That seems right. When you write auto x = y; you'd certainly expect x to not be top-level const, but here we have a situation where it still is. I don't think there's a Core issue open for this yet, but there should be.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • The cv-qualification _cv_ comes from the decl-specifier-seq ("`auto`") not the initialiser ("`ar`"), no? Unless it's saying that the qualifiers on `A` are merged into _cv_. But I don't follow why that would be the case; without a ref-qualifier, why would it want to maintain constness? It doesn't do that anywhere else. – Lightness Races in Orbit Dec 11 '18 at 16:21
  • 5
    @LightnessRacesinOrbit It does - but `A` is an array of const, so `cv A` is still an array of const. I don't think we want to maintain constness, hence Core issue. – Barry Dec 11 '18 at 16:25
  • Mm, I could be persuaded by that. – Lightness Races in Orbit Dec 11 '18 at 16:56