Search code examples
c++c++17structured-bindings

Type of reference binding from std::tuple


What is the type of a here?

#include <iostream>
#include <tuple>

using namespace std;

int main()
{
    float x{};
    std::tuple<int> tpl( x );
    auto& [ a ] = tpl;
    static_assert( std::is_same_v< decltype( a ), int> );
    //static_assert( std::is_same_v< decltype( a ), int&> );
}

According to Standard 11.5/3:

[...] Given the type Ti designated by std::tuple_element<i, E>::type, variables are introduced with unique names ri of type “reference to Ti” initialized with the initializer (11.6.3), 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.

Here, i is 0 for the first element (int) and E is std::tuple<int>, so Ti has type std::tuple_element<0, std::tuple<int>>::type, i.e. int. Further ri (a in our case) has type "reference to Ti", i.e. lvalue reference int&, but not just int. What is wrong in that quote and why int type is deduced by both the compilers clang and gcc?


Solution

  • It's true that the type of variable r0 is int&. But v0, here called a, is a different thing, the name of a structured binding, and is not technically a variable at all, just a different sort of name.

    So we need to look at the description of decltype, in [dcl.type.decltype]:

    For an expression e, the type denoted by decltype(e) is defined as follows:

    • if e is an unparenthesized id-expression naming a structured binding, decltype(e) is the referenced type as given in the specification of the structured binding declaration;

    So here the "referenced type" is just int.

    Of course, decltype((a)) is int& as we'd expect. decltype on a name without doubled parentheses is used to find out how a name is "declared", not how the name behaves, so it's not obvious that decltype(a) "should" be one thing or another, since a doesn't have a normal declaration. Though it is potentially a bit useful that given std::tuple<int, int&> t{0, n}; auto& [a, b] = t;, we have decltype(a) is int but decltype(b) is int&.