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

Is decltype(auto) for a structured binding supposed to be a reference?


Consider an example:

#include <iostream>
#include <type_traits>
#include <tuple>

int main() {
    auto tup = std::make_tuple(1, 2);
    auto [ a, b ] = tup;
    decltype(auto) e = a;
    std::cout << std::boolalpha << std::is_reference_v<decltype(e)> << std::endl;
}

clang (output: false) and gcc (output: true) are disagreeing in this simple case. Having in mind e.g. this Q&As should the e be a reference or is it a gcc bug? Or maybe the code is ill-formed?


Solution

  • The identifers themselves are references. From [dcl.struct.bind]/3:

    Given the type Ti designated by std​::​tuple_­element<i, E>​::​type, each vi is a variable of type “reference to Ti” initialized with the initializer, where the reference is an lvalue reference if the initializer is an lvalue and an rvalue reference otherwise; the referenced type is Ti.

    That is, a and b are both int&&.

    But the way decltype(auto) actually behaves comes from [dcl.type.auto.deduct]:

    If the placeholder is the decltype(auto) type-specifier, T shall be the placeholder alone. The type deduced for T is determined as described in [dcl.type.simple], as though e had been the operand of the decltype.

    This wording is really awkward, but ultimately:

    decltype(auto) e = a;
    ~~~~~~~~~~~~~~
    

    means:

    decltype( a  ) e = a;
             ~~~~
    

    and decltype(a) means, from [dcl.type.simple]/4.1:

    if e is an unparenthesized id-expression naming a structured binding ([dcl.struct.bind]), decltype(e) is the referenced type as given in the specification of the structured binding declaration;

    The referenced type of a is int, so e must be an int. Which means it's not a reference, and clang is correct. Filed 81176.