18

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&?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • 14
    with `const`, you would have "`int & const`" which is `int&`. – Jarod42 Aug 07 '18 at 06:20
  • 2
    Because `std::tuple_element<1, const std::tuple>::type` is `int&`. – cpplearner Aug 07 '18 at 11:11
  • I could be wrong but after the reading I've done about this it seems to be more related to the whole 'logical' `const` vs 'physical' `const` issue that crops up in C++. In this case the variable introduced by the compiler with structured bindings is itself `const`, but any pointers or references won't be as they are 'outside' of the object. (I'm attempting to describe the issue where you can modify an object through a pointer in a `const` member function). – Tom Mar 01 '20 at 20:21

2 Answers2

15

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

Given the type Ti designated by std​::​tuple_­element​::​type, variables are introduced with unique names ri of type “reference to Ti” initialized with the initializer ([dcl.init.ref]), where the reference is an lvalue reference if the initializer is an lvalue and an rvalue reference otherwise. Each vi is the name of an lvalue of type Ti that refers to the object bound to ri; the referenced type is 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.

¹ Thanks to @cpplearner for pointing me to the exact paragraph here.

lubgr
  • 37,368
  • 3
  • 66
  • 117
5

It would be the reference itself rather than the referenced value that would be const. As references aren't modifiable anyway there is no such thing as a constant reference.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60