Search code examples
c++templatesc++17return-valuedecltype

Decltype of function call discards const qualifiers


Consider a function

template <typename T>
    const T&& foo();

When i had tried to tested it,i founded result that I didn't understand. Output is 0.

#include <iostream>

class C{};

template <typename T>
    const T&& foo();

int main() {
    std::cout << std::is_same_v<decltype(foo<C&&>()), const C&&>;
    return 0;
}

Shouldn't decltype preserve const qualifiers? In my opinion, it should be like T-> C&&, so return type is const C&& via reference collapsing rules, and decltype of result is const C&&

Also, this line

std::cout << std::is_same_v<decltype(foo<int>()), const int&&> << '\n';

Prints 1 as expected.

Can somebody help me to figure out correct logic of working?

I found that when decltype applied on non-class prvalue, cv qualifiers are discarded. But this is not the case, because value category of foo<int&&>() is xvalue, and this also doesn't works with class type C.


Solution

  • Shouldn't decltype preserve const qualifiers?

    Yes, and it does here as well. The return type of foo<C&&>() really is C&&, not const C&&.

    In my opinion, it should be like T-> C&&, so return type is const C&& via reference collapsing rules, and decltype of result is const C&&

    No, you are applying const to T first, then && afterwards.

    const T&& means "rvalue reference to (const T)". It doesn't mean "const (rvalue reference to T)".

    The const is applied at top-level to T. That means if T is C&& you are trying to const-qualify a reference, but there are no const-qualified references in C++. So the language rules say that const is ignored when trying to const-qualify a reference through such a construction.


    std::cout << std::is_same_v<decltype(foo()), const int&&> << '\n';

    Here you are applying const to int giving you const int and then you add && on top. So you get const int&&.

    I found that when decltype applied on non-class prvalue, cv qualifiers are discarded.

    They are discarded on the non-class type prvalue expression, but not on the return type of the function itself. If a function is declared to return const T and you substitute int for T, then the function has return type const int, but a call to the function is a prvalue expression of type int.

    But this is not the case, because value category of foo<int&&>() is xvalue, and this also doesn't works with class type C.

    Yes, correct. This special rule doesn't matter here. decltype does faithfully reproduce the declared return type.