Why does “const auto [x, y]” not behave as expected when binding to reference types?

  • A+

The following code snippet is excerpted from cppref:

std::tuple<int, int&> f();  auto [x, y] = f();  // decltype(x) is int // decltype(y) is int&  const auto [z, w] = f(); // decltype(z) is const int // decltype(w) is int& 

My question is at the last line:

Why is decltype(w) int& rather than const int&?


Jarod42 answered the question the question in the comments, let me just cite the relevant part of the standard here, from [dcl.struct.bind]:

Designating the non-static data members of E as m0, m1, m2, ... (in declaration order), each vi is the name of an lvalue that refers to the member mi of e and whose type is cv Ti, where Ti is the declared type of that member; the referenced type is cv Ti.

Hence in const auto [z, w] = f();, you have const T1 with T1 being int and const T2 with T2 being int&. As const modifies what's on its left, this becomes int& const and results in int&.

Note that int& const becoming int& is only possible in template argument substitution, i.e., this won't compile:

int n = 42; int& const doesntWork = n; // Error: 'const' qualifiers cannot be applied to 'int&' 

but this does:

template <class T> void f(const T t) {    ++t; }  int n = 42;  f<int&>(n); 

where the identical contraction from int& const to int& as above takes place.


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: