The standard type_traits library produces unexpected beavior with some type aliases that use decltype

Let me illustrate. Let’s take std::is_same_v and std::is_base_of_v for example. Consider the following code:

#include <iostream>
#include <array>
#include <type_traits>

using namespace std;

struct base {};
struct derived : base { int foo; };

array<derived, 10> my_array;

int main()
{
    using c1 = decltype(*begin(my_array));
    using c2 = derived;

    if constexpr(is_same_v<c1,c2>)
    {
        cout<<"Correct!"<<endl;
    }
    else
    {
        cout << "No luck even though:" << endl
             << "c1 type is " << typeid(c1).name() << endl
             << "c2 type is " << typeid(c2).name() << endl;
    }

    if constexpr(is_base_of_v<base, c1>)
    {
        cout<<"Correct!"<<endl;
    }
    else
    {
        cout << "No luck even though:" << endl
             << "is_base_of_v<base, derived> = " << is_base_of_v<base, derived> << endl;
    }

    return 0;
}

The expected result is:

Correct!
Correct!

But, the actual result in both clang 10 and gcc 11 is:

No luck even though:
c1 type is 7derived
c2 type is 7derived
No luck even though:
is_base_of_v<base, derived> = 1

Mind blown. I am primarily interested in finding out the exact cause behind this behavior and then maybe find a workaround. If possible the workaround should work with any iterable type and just std::array.

Answer

Your issue is that you have extra reference:

using c1 = decltype(*begin(my_array)); // derived&
using c2 = derived;

is_same_v<derived, derived&> is false.

std::decay_t or std::remove_reference_t might help.

And the typeid are identical:

  1. Refers to a std::type_info object representing the type type. If type is a reference type, the result refers to a std::type_info object representing the referenced type.

Another way to know the type is to use that trick:

template <typename> struct Debug; /*No definition*/

Debug<c1> d; // Incomplete type

with error message similar to

error: aggregate ‘Debug<derived&> d‘ has incomplete type and cannot be defined

Demo