c++thistype-traitsdecltypeis-same

Why doesn't decltype(*this)::value_type compile?


Why doesn't decltype(*this) compile? It shows an error message:

error: 'value_type' is not a member of 'const Foo<char>&'

So what exactly is the reason that decltype( *this )::value_type does not compile in the below program:

#include <iostream>
#include <vector>
#include <type_traits>


template <typename charT>
struct Foo
{
    using value_type = charT;

    std::vector<value_type> vec;

    void print( ) const;
};

template <typename charT>
void Foo<charT>::print( ) const
{
    using Foo_t = std::remove_reference_t<decltype( *this )>;

                                   // `decltype( *this )::value_type` does not compile
    if constexpr ( std::is_same_v< typename Foo_t::value_type,
                                   decltype( std::cout )::char_type > )
    {
        // logic
    }
    else if constexpr ( std::is_same_v< typename Foo_t::value_type,
                                        decltype( std::wcout )::char_type > )
    {
        // logic
    }
    else
    {
        static_assert( std::is_same_v< typename Foo_t::value_type,
                                       decltype( std::cout )::char_type > ||
                       std::is_same_v< typename Foo_t::value_type,
                                       decltype( std::wcout )::char_type >,
                       "character type not supported" );
    }
}

int main( )
{
#define IS_CHAR 1

#if IS_CHAR == 1
    using FooChar = Foo<char>;
    FooChar foo;
    foo.vec.resize( 10, '$' );
#else
    using FooWideChar = Foo<wchar_t>;
    FooWideChar foo;
    foo.vec.resize( 10, L'#' );
#endif

    foo.print( );
}

What is special about the this pointer? Why does removing the reference with std::remove_reference_t make it compile? Everything works in the above snippet. But if I replace typename Foo_t::value_type with the more readable decltype( *this )::value_type it won't compile. So I tried my luck by using std::remove_reference_t and managed to come up with the above less straightforward (and less intuitive) solution:

using Foo_t = std::remove_reference_t<decltype( *this )>;

// and then inside the std::is_same_v
std::is_same_v< typename Foo_t::value_type, /* etc */ >
// ...

I know that there are more concise alternatives e.g. std::is_same_v< Foo<charT>::value_type, or even std::is_same_v< charT, but I find the decltype approach more self-explanatory. Are there any other solutions?


Solution

  • The result of applying unary operator* to a pointer is a reference (lvalue ref) to the pointed at value, not a copy of the pointed at value. So decltype(*this) (or decltype(*foo) for any pointer type) will always be a reference type. There's nothing special about this.