2

I'm so confused about this template I wrote, It's supposed to 'auto' deduce a pointer type that I pass in, but the compiler doesn't seem to think that two types are the same, whereas I do, and typeid() is on my side.

#include <iostream>

template <auto* static_ptr_to_object>
struct Handle
{
    // static_ptr_to_object should be a pointer to a Whale<char>
    // Both of the following typedefs should result in types of Whale<char>
    // When I cout typeid() of these types it prints out the same types
    // However std::is_same_v returns false

    using pointee_type1 = std::remove_pointer_t<decltype(static_ptr_to_object)>;
    using pointee_type2 = decltype(*static_ptr_to_object); // The two are the same types

    void showTypes()
    {
        //static_assert(std::is_same_v<pointee_type1, pointee_type2>);
        // Uncommenting this line will make the static assert fail

        std::cout << "Type of 'pointee_type1' = " << typeid(pointee_type1).name() << "\n";
        std::cout << "Type of 'pointee_type2' = " << typeid(pointee_type2).name() << "\n";

        if (typeid(pointee_type1) == typeid(pointee_type2))
            std::cout << "Types are the same\n";
        else std::cout << "Types are not the same\n";

    }

    bool b1 = std::is_integral_v<decltype(pointee_type1::member)>;
    // Uncommenting the below line will make compilation fail

    //bool b2 = std::is_integral_v<decltype(pointee_type2::member)>;
    // pointee_type2 must be a class or namespace when followed by ::
    // pointee_type2 left of '::' must be a class or namespace name
    

};

template <typename T>
struct Whale
{
    T member;
};

Whale<char> whale;

int main()
{
    Handle<&whale> handleToWhale;
    handleToWhale.showTypes();
}

So I think the two types are the same, and typeid() operator says they are the same, yet std::is_same returns false, and based on the compiler error message it doesn't recognise that pointee_type2 is a class type because it doesn't recognise the :: operator for it.

Edit: My understanding about dereferencing pointers was wrong, this question relates to how a pointer dereference will return a reference type in some cases.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Zebrafish
  • 11,682
  • 3
  • 43
  • 119
  • 2
    Fun fact about `name`: [It gives no guarantee of uniqueness](https://en.cppreference.com/w/cpp/types/type_info/name). – user4581301 May 25 '21 at 14:21

2 Answers2

4

For decltype,

  1. 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. ......

  2. If the argument is any other expression of type T, and

    a) ...
    b) if the value category of expression is lvalue, then decltype yields T&;
    c) ...

*static_ptr_to_object fits the 2nd case, and it's an lvalue expression, so decltype(*static_ptr_to_object) yields a reference type, i.e. Whale<char>&, pointee_type1 and pointee_type2 are not the same type.

You can use std::remove_reference to get the same type as:

using pointee_type2 = std::remove_reference_t<decltype(*static_ptr_to_object)>;

On the other hand, typeid does yield same result (the same std::type_info object).

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

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • So it's not a template thing, dereferencing a pointer gives you a reference to whatever it was pointing to, and decltype of any pointer dereferenced will be a reference to whatever type was pointed to? – Zebrafish May 25 '21 at 14:29
  • 2
    @Zebrafish `decltype` works in two different ways. For id-expression, it yields the exact type of the entity named by the id-expression. For other expression, it gives result not only according to the type but also the value category of the expression. If dereference on pointer leads to an lvalue expression, then you'll get `T&`. – songyuanyao May 25 '21 at 14:34
3

pointee_type1 is Whale<char>, but pointee_type2 is Whale<char> &. typeid removes the reference, so you get the same name.

Caleth
  • 52,200
  • 2
  • 44
  • 75