Home > Back-end >  How does `std::is_const` work on non-static member method types?
How does `std::is_const` work on non-static member method types?

Time:11-24

Question:

How does std::is_const work on non-static member method types? Is a const member method not a const-qualified type?

Example:

class D {};

We will have

std::is_const_v<void (D::*)() const> == false

Follow-up:

Can the constness of a member non-static method be determined (at compile/run time)?

CodePudding user response:

I don't think there is a standard trait to check for this type of constness. But it is a relatively straight forward one to write :

#include <type_traits>

// Base template
template<class T>
struct is_const_member_func_ptr;

// Specialize for pointers to non-const member function
template<class T, class C, class ... A>
struct is_const_member_func_ptr<T (C::*)(A...)> : std::false_type {};

// Specialize for pointers to const member function
template<class T, class C, class ... A>
struct is_const_member_func_ptr<T (C::*)(A...) const> : std::true_type {};

// Convenient constant template
template<class T>
inline constexpr bool is_const_member_func_ptr_v = is_const_member_func_ptr<T>::value;

Usage :

class D {};

static_assert(is_const_member_func_ptr_v<void (D::*)() const> == true);
static_assert(is_const_member_func_ptr_v<void (D::*)()>       == false);

Live example : https://godbolt.org/z/Y9EjY5jzf


Edit : Example using an actual defined member function

// Usage
#include <iomanip>
#include <iostream>

class D 
{
public:
    void foo() {}
    void foo_c() const {}
};

int main()
{
    std::cout << std::boolalpha << is_const_member_func_ptr_v<decltype(&D::foo)> << '\n';
    std::cout << std::boolalpha << is_const_member_func_ptr_v<decltype(&D::foo_c)> << '\n';
}

Live : https://godbolt.org/z/9fvYs834W

CodePudding user response:

In C , functions are not first-class citizens: you can't have objects with function type (you can have function objects, but that's a different concept).

void (D::*)() const is a pointer type. std::is_const will tell you whether the pointer is const or not, so:

  • std::is_const_v<void (D::*)() const> == false;
  • std::is_const_v<void (D::* const)()> == true.

There is no standard trait to check whether a member function has a const qualifier. However, you can make your own. François Andrieux's answer would be the traditional way of doing that, but as mentioned in the comments, you need 48 specializations to make it complete.

Here is another, perhaps more concise way:

#include <type_traits>

struct ArbitraryType {
    template <typename T>
    operator T & ();
    template <typename T>
    operator T && ();
};

template<bool, bool, class T, class Arg, class... Args>
struct is_invocable_with_const_first_arg_impl : std::bool_constant<
    is_invocable_with_const_first_arg_impl<
        std::is_invocable_v<T, Arg const&, Args...> || std::is_invocable_v<T, Arg const&&, Args...>,
        std::is_invocable_v<T, Arg&, Args...> || std::is_invocable_v<T, Arg&&, Args...>,
        T, Arg, Args..., ArbitraryType
    >::value
> {};

template<bool b, class T, class Arg, class... Args>
struct is_invocable_with_const_first_arg_impl<true, b, T, Arg, Args...> : std::true_type {};

template<class T, class Arg, class... Args>
struct is_invocable_with_const_first_arg_impl<false, true, T, Arg, Args...> : std::false_type {};

template<class T, class Arg>
struct is_invocable_with_const_first_arg : is_invocable_with_const_first_arg_impl<false, false, T, Arg> {};

template<class T, class Arg>
inline constexpr bool is_invocable_with_const_first_arg_v = is_invocable_with_const_first_arg<T, Arg>::value;

template<class T>
struct is_const_member_func_ptr;

template<class R, class C>
struct is_const_member_func_ptr<R C::*> : std::bool_constant<
    is_invocable_with_const_first_arg_v<R C::*, C>
    || is_invocable_with_const_first_arg_v<R C::*, C>
> {};

template<class T>
inline constexpr bool is_const_member_func_ptr_v = is_const_member_func_ptr<T>::value;

Demo

  •  Tags:  
  • c
  • Related