Type of member variables in a const member function

decltype has special rule for class members. It returns the actual type of the member. If you want decltype to consider the context (inside a const function), then you can wrap the expression inside parentheses.

Without Paranthesis:

 void test() const {
        type_printer<decltype(value)> _;
    }
c.cpp:10:39: error: implicit instantiation of undefined template 'type_printer<int>'
        type_printer<decltype(value)> _;

With Paranthesis:

 void test() const {
        type_printer<decltype((value))> _;
    }

c.cpp:10:41: error: implicit instantiation of undefined template 'type_printer<const int &>'
        type_printer<decltype((value))> _;

Reference:

https://en.cppreference.com/w/cpp/language/decltype

If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression. If there is no such entity, or if the argument names a set of overloaded functions, the program is ill-formed.

https://docs.microsoft.com/en-us/cpp/cpp/decltype-cpp?view=vs-2019

If the expression parameter is an identifier or a class member access, decltype(expression) is the type of the entity named by expression. If there is no such entity or the expression parameter names a set of overloaded functions, the compiler yields an error message.