c++templatestypesmetaprogrammingtypeid

Why does typeid refuse to work for function types with const at the end?


So I've got this code:

template <typename T, typename class_t>
struct remove_ptr_to_member { using type = T; };

template <typename T, typename class_t>
struct remove_ptr_to_member<T class_t::*, class_t> { using type = T; };

class container {
public:
    void func() const;
};

void print_member_type() {
    std::cout << typeid(typename remove_ptr_to_member<decltype(&container::func), container>::type).name() << '\n';
}

I get this error message when trying to compile it:

error: non-member function of type 'typename remove_ptr_to_member<decltype(&container::func), container>::type' (aka 'void () const') cannot have 'const' qualifier

As I understand it, the const at the end of the function is part of it's type. Is typeid not accepting it because it's an invalid type (since non-member functions can't have const at the end)? Why is it that I can create a type that shouldn't actually exist? How would I go about removing the const, so as to make everything compile properly?


Solution

  • A function type with const or other cvref-qualifiers is a valid type in the language, but it has very limited uses, mainly to declare non-static member functions and to form pointers to non-static member functions.

    Therefore the standard has restrictions on where they may be used, listed in [dcl.fct]/6. In particular this list does not include the operand of typeid. As a consequence you are not allowed to use such a function type as operand of typeid.

    This was also subject of an editorial issue which resulted in a note being added to the description of typeid in the standard to make it clear that this is not permitted.

    So, although there is nothing wrong with how you obtain the function type, you can't use it as typeid operand.


    Unfortunately I don't think there is any type trait in std:: that allows you to remove the cvref qualifiers from the function type.

    These can be implemented by yourself, but implementing such traits on qualified function types is notoriously tedious because there are (if I didn't miscount) 24 overloads or partial specializations for all possible combinations of cvref and noexcept qualifiers required. (Actually 48 to include variadic functions.)

    See for example this older question about it. However note that this question is pre-C++17. Since C++17 function types with and without noexcept need to also be differentiated.

    Instead you might want to have a look at Boost.CallableTraits which has a list of remove_* type traits which you can use to remove the const/volatile/&/&&-qualifiers.