Zeenobit
Zeenobit

Reputation: 5194

Implementation of std::is_function with cv-qualifiers and ref-qualifiers

According to the C++ reference, this is a valid implementation of std::is_function (excluding the partial specializations for variadic functions and noexcept specifiers for brevity):

template<class>
struct is_function : std::false_type { };
 
// specialization for regular functions
template<class Ret, class... Args>
struct is_function<Ret(Args...)> : std::true_type {};
 
// specialization for function types that have cv-qualifiers
template<class Ret, class... Args>
struct is_function<Ret(Args...)const> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...)volatile> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...)const volatile> : std::true_type {};
 
// specialization for function types that have ref-qualifiers
template<class Ret, class... Args>
struct is_function<Ret(Args...) &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...)const &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...)volatile &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...)const volatile &> : std::true_type {};
struct is_function<Ret(Args...) &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...)const &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...)volatile &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...)const volatile &&> : std::true_type {};

However, using std::is_function on member functions returns false:

struct X
{
    int Test(float)
    {
        return 0;
    }
};

int main()
{
    auto x = std::is_function_v<decltype(&X::Test)>; // x is 'false'
    return 0;
}

As far as I understand it, cv-qualifiers and ref-qualifiers are only applicable to class member functions.

So my question is, why does the implementation of std::is_function specialize for all the different cv-qualifiers and ref-qualifiers when it doesn't consider member functions "functions" to begin with?

Update:

Based on the answer below, I decided to do an experiment. I implemented my own minimal version of std::is_function:

template <class T>
struct IsFunction :
    std::integral_constant<bool, false>
{
};

template <class R, class... A>
struct IsFunction<R(A...)> :
    std::integral_constant<bool, true>
{
};

template <class T>
constexpr bool IsFunctionV = IsFunction<T>::value;

And then I changed the signature of X::Test:

struct X
{
    int Test(float) const
    {
        return 0;
    }
};

Using the function_traits struct provided in the answer, then I tried this:

auto x = IsFunctionV<function_traits<decltype(&X::Test)>::type>;

In this case, x is false. But if I add a specialization for const to my IsFunction as such:

template <class R, class... A>
struct IsFunction<R(A...) const> :
    std::integral_constant<bool, true>
{
};

Then x will be true! So the overload is important. But I'm not sure I understand why, or how function_traits ends up converting a "member function pointer" to a "member function", which isn't really the same as a regular function, is it...?

Upvotes: 2

Views: 307

Answers (2)

user743382
user743382

Reputation:

As far as I understand it, cv-qualifiers and ref-qualifiers are only applicable to class member functions.

Although non-member functions cannot have cv-qualifiers or ref-qualifiers, function types can still contain them without being bound to a specific class type.

typedef void fc() const;
struct S { fc f; };
void S::f() const { }

Here, std::is_function_v<fc> is supposed to be true.

Upvotes: 4

Henri Menke
Henri Menke

Reputation: 10939

This is not a direct answer to the question but shows how you can remove the class from the member function type.


You can remove the class instance from the member function by a simple type trait to make the function compatible with is_function. This is clearly necessary because

Types like std::function, lambdas, classes with overloaded operator()and pointers to functions don't count as function types.

http://en.cppreference.com/w/cpp/types/is_function

#include <iostream>
#include <type_traits>

template < typename T >
struct function_traits { typedef T type; };

template < typename T, typename C >
struct function_traits < T(C::*) > { typedef T type; };

struct X
{
    int Test(float)
    {
        return 0;
    }
};

int test(float) { return 1; }

int main()
{
    std::cout << std::boolalpha;

    auto x = std::is_function< decltype(&X::Test) >::value;
    std::cout << x << '\n';

    auto y = std::is_function< function_traits<decltype(&X::Test)>::type >::value;
    std::cout << y << '\n';

    auto z = std::is_function< function_traits<decltype(test)>::type >::value;
    std::cout << z << '\n';
}

Demo on Wandbox

Upvotes: 2

Related Questions