Search code examples
c++language-lawyerdecltype

decltype on the variable of reference type with curly braces


Consider the following code:

#include <type_traits>

int main() {
    const int& p = 42;
    auto v1 = decltype(p){};
    static_assert(std::is_same_v<decltype(v1), int>);
    
    decltype(p) v2{};
    static_assert(std::is_same_v<decltype(v2), const int&>);
    // auto v3 = X(const int&)X {};
}

Type of v1 is deduced as int. At the same time type of v2 is expectedly deduced as const int&. I think the first step for v1 could be treated as adding one more type alias using T = decltype(p); and then auto v4 = T{};. How this expression (decltype(p){} or T{}) is treated by compiler? I know that {} part is for instantiation, but how the resulting type of v1 is not a reference type?

Yet another question: is there a way to declare v3 variable of the same type as v1 using explicitly noted type const int& (instead of decltype(p))?

Any links to standard would be appreciated.


Solution

  • (To the downvoter(s): if you downvoted because you think quoting Scott Meyers is not equivalent to quoting the standard, oh well...)

    As you can read from Effective Modern C++ (augmented with the part of the errata that you can reach by searching for Case 2: at that link, and that just makes the following read simpler, but it's not essential to the question):

    If ParamType is a non-reference [...] if expr's type is a reference, ignore the reference part. If [...] expr is const, ingore that too. If it's volatile, also ignore that.

    where param is the declaration specifier, which in your case is just auto, i.e. a non-reference.

    In other words, you're creating v1 via plain auto (not auto&), i.e. by copy, so it does not matter whether you are initializing it with an entity which is reference or not, or even with const or not (or volatile or not, fwiw), because you're copying it.

    Think about the simpler case,

    int i = 3;
    int& p = i;
    auto v1 = p;
    

    as far as v1 is concerned, it's really not important whether it is initalized with one (i) or the other (p) name by which the same entity is known, because it will get a copy of whatever value that entity has.

    auto type deduction works just like template type deduction (except for a difference in how they deal with braced initializer, which is not relevant in this case), and for both of them you can refer to Scott Meyers' Effective Modern C++.