Why does this const function return a non-const reference?

141 Views Asked by At

In this code, why is

  1. the constness of GetAutoRef and GetAutoRefJ's return values different,
  2. the return value of GetDecltypeAutoJ not const?
#include <type_traits>

struct A {
    int i;
    int& j = i;

    decltype(auto) GetDecltypeAuto() const { return i; }
    auto           GetAuto        () const { return i; }
    auto&          GetAutoRef     () const { return i; }

    decltype(auto) GetDecltypeAutoJ() const { return j; }
    auto           GetAutoJ        () const { return j; }
    auto&          GetAutoRefJ     () const { return j; }
};

int main() {
 A a{5};
 static_assert(std::is_same_v<decltype(a.GetDecltypeAuto()), int>       );
 static_assert(std::is_same_v<decltype(a.GetAuto()        ), int>       );
 static_assert(std::is_same_v<decltype(a.GetAutoRef()), const int&>); //as expected

 static_assert(std::is_same_v<decltype(a.GetDecltypeAutoJ()), int&>); // no const?
 static_assert(std::is_same_v<decltype(a.GetAutoJ()        ), int> );
 static_assert(std::is_same_v<decltype(a.GetAutoRefJ()     ), int&>); // no const?
}

Shouldn't j be const if accessed through the const this pointer in the J functions?

https://godbolt.org/z/3v4PKG5n3

2

There are 2 best solutions below

0
On BEST ANSWER

As NathanOliver explained, the const is applied to the reference itself, not the referenced type.

This might seem confusing, and it might help to remember that reference are mostly just "convenient pointers". If you use a pointer instead, things become more obvious:

struct A {
    int i;
    int& j = i;
    int* k = &i;

    decltype(auto) GetDecltypeAutoJ() const { return j; }
    decltype(auto) GetDecltypeAutoK() const { return *k; }
};

In GetDecltypeAutoK, the type of this->k is int* const, i.e. not int const*. Dereferencing an int* const gives you an int.

It's the same in GetDecltypeAutoJ: the type of this->j is int& const which is identical to int& since references are always constant.

6
On

You can't apply const to a reference, and that is what the const of the member function tries to apply const to. So what you are getting is the type of j which will always be int& for decltype(auto) or int when just using auto.

You can see this by changing the value of j in your const functions and the compiler will not complain.