Reputation: 5194
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?
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
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
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 overloadedoperator()
and pointers to functions don't count as function types.
#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';
}
Upvotes: 2