has_member
¶
Does T
have a member function foo(int)
?
There are a few cases where you want to check if some type has a member function with a given name and signature. In this article, I will present an easy-to-use framework for making your own member checkers. First, I will present a non-generic checker so I can explain how and why it works. Then, I will present a generic checker which uses the same techniques to make a system which is easy to use with your own types and function signatures.
The goal of this article is a type where has_foo<T>::value
will evaluate to true
if the type T
has a
function foo
and false
if it does not.
A “Simple” Example¶
To make things a little less painful, let’s start with a type which can only check for a single function.
In this class, we check that there is a void T::foo(int)
.
template <typename T>
class has_foo
{
private:
template <typename U, U>
class check
{ };
template <typename C>
static char f(check<void (C::*)(int), &C::foo>*);
template <typename C>
static long f(...);
public:
static const bool value = (sizeof(f<T>(0)) == sizeof(char));
};
Okay, so that looks a little funny, but stick with me for a bit.
Example: A False Case¶
Let’s step through this when the compiler instantiates the template, starting with a class which does not have a
foo(int)
.
struct No
{ };
The only thing defined is value
, which contains the interesting subexpression sizeof(f<T>(0))
.
This means to get the size of the return type for the given subexpression (f<T>(0)
).
In this class, there are two functions with the name f
, so the question is: Which f
would f<T>(0)
call?
To find out, the compiler finds template functions named f
.
One such f
is:
template <typename C>
static char f(check<void (C::*)(int), &C::foo>*);
This f
is looking for a pointer to some funny-looking template class check
.
Now, the first template parameter names a type: void (C::*)(int)
– a pointer to a member function on C
which
takes an int
and returns void
.
The compiler is happy with this first parameter, because there could be a function like that.
The second template parameter &C::foo
selects the function foo
on C
.
Uh-oh: No
, the provided C
, does not have a function named foo
and the compiler has a substitution failure.
However, substitution failure is not an error
comes to the rescue.
Instead of breaking and giving you a 100-line error message, this failed substitution means
template <typename C> static char f(check<void (C::*)(int), &C::foo>*)
is just thrown out.
The remaining candidate for overload resolution is template <typename C> static long f(...)
, which is not terribly
interesting.
The ...
takes any type of argument, so it will work just fine for f<T>(0)
.
The return type of this function is long
, leaving value = (sizeof(long) == sizeof(char))
.
Ultimately, this yields value = false
and everything is good to go.
Example: A True Case¶
What does the compiler do on a class which has the correct void foo(int)
?
struct Yes
{
void foo(int x);
};
Let’s start with the place where the false case had a substitution failure.
template <typename C>
static char f(check<void (C::*)(int), &C::foo>*);
Unlike before, the expression &C::foo
is valid and we have a function which is looking for a pointer to the
check
object.
The function template <typename C> static char f(...)
is, of course, still under consideration, which means the
compiler has found two possible f
:
char f(check*)
long f(...)
The compiler must now decide which f
you meant to call with the expression f<T>(0)
, so it evaluates the possible
types for the parameters.
Can the argument 0
be a check*
?
Yes!
0
is the null pointer.
Likewise, 0
can be the single parameter to the varargs f(...)
.
Luckily enough, this does not mean that f<T>(0)
is ambiguous.
Interpreting 0
as the null pointer is more specific than interpreting it as an argument in a varargs list (in
fact, almost anything is more specific), so the compiler will see f<T>(0)
as a call to
template <typename C> static char f(check<void (C::*)(int), &C::foo>*)
.
The return type of the function is char
, leaving value = (sizeof(char) == sizeof(char)), making ``value = true
.
Example: The Wrong Type¶
What about a class which has a function named foo
, but with the wrong signature?
struct Wrong
{
void foo(double x);
};
Here, the definition of check
comes into play.
template <typename U, U>
class check
{ };
The type for the first template parameter U
will be void (Wrong::*)(int)
.
The second template parameter expects some U
, which the signature of f
demands it be &C::foo
.
However, &Wrong::foo
is a void (Wrong::*)(double)
, which leads to a substitution failure.
Real Implementation: Checker Class¶
While has_foo
certainly works, making another class has_bar
or has_baz
would require a bit of copy and paste
work.
The definition of the signature and name of the member function is sibling to the strange-looking logic of SFINAE.
Without documentation, it is not obvious what this class is even used for or how it works.
Ideally, we should be able to define new checkers more like this:
template <typename T>
struct has_foo : has_member<T, check_has_foo>
{ };
It can be done!
#include <type_traits>
namespace detail
{
template <typename T, typename NameGetter>
struct has_member_impl
{
typedef char matched_return_type;
typedef long unmatched_return_type;
template <typename C>
static matched_return_type f(typename NameGetter::template get<C>*);
template <typename C>
static unmatched_return_type f(...);
public:
static const bool value = (sizeof(f<T>(0)) == sizeof(matched_return_type));
};
}
template <typename T, typename NameGetter>
struct has_member :
std::integral_constant<bool, detail::has_member_impl<T, NameGetter>::value>
{ };
The has_member_impl
class looks similar to the has_foo
class.
The has_member
class gives us a way to make std::true_type
and std::false_type
.
The big technical difference is to the matcher:
template <typename C> static matched_return_type f(typename NameGetter::template get<C>*)
.
(<a href=”#info-typename-template”>4</a>). This pushes responsibility of matching <tt>T</tt> outside of the class and
into the new type parameter <tt>NameGetter</tt>.
Note
What’s with ``typename NameGetter::template get<C>``?
The typename
is needed here because we are using NameGetter
dependent context, which is a fairly common
thing to see.
The less common thing to see is the extra template
before get
.
If that is not there, g++ gives you error: non-template 'get' used as template
along with the friendly
note: use 'NameGetter::template get' to indicate that it is a template
.
It is fairly easy for a human to look at this code and say: Clearly, my check_print::get
is a template, the
compiler is stupid!
However, when you say typename NameGetter::get
, you are explicitly telling the compiler that NameGetter::get
names a type, not that it names a templated type.
A NameGetter
might look something like this:
struct check_has_foo
{
template <typename T,
void (T::*)(int) = &T::foo
>
struct get
{ };
};
Now, the responsibility of checking the function signature is in it’s own class.
This separation of data and logic makes for easy reuse.
If we wanted to check for a function bar
which took two float
, returned an int
and operated on const T
,
we could do that:
struct check_has_bar
{
template <typename T,
int (T::*)(float, float) const = &T::bar
>
struct get
{ };
};
template <typename T>
struct has_bar : has_member<T, check_has_bar>
{ };
The interesting thing is that there is no requirement that we only check for one function. In fact, it is quite easy to check for a number of functions:
struct check_read_write
{
template <typename T,
int (T::*)() const = &T::read,
void (T::*)(int) = &T::write
>
struct get
{ };
};
template <typename T>
struct has_bar : has_member<T, check_has_bar>
{ };
Real-World Use¶
Let’s say you wanted to overload operator<<
to call the print
function on a bunch of objects (which can be
useful when you want to print polymorphically).
You can not just write a function like this:
template <typename T>
std::ostream& operator<<(std::ostream& stream, const T& value)
{
value.print(stream);
return stream;
}
The problem is that every type would match against that function definition, including types like int
, which will
fail marvelously inside the function.
What you need is a way to enable operator<<
only for types which have your print
function.
Luckily, we have std::enable_if
! First, let’s define the class which checks for the print function.
struct check_has_print
{
template <typename T,
void (T::*)(std::ostream&) const = &T::print
>
struct get
{ };
};
template <typename T>
struct has_print :
has_member<T, check_has_print>
{ };
Now, we can use the checker with enable_if
:
template <typename T>
typename std::enable_if<has_print<T>::value, std::ostream&>::type
operator<<(std::ostream& stream, const T& value)
{
value.print(stream);
return stream;
}
This works under the same principles of substitution failure is not an error.
When the first value is false
, the member type
is not defined, causing substitution failure on the overload for
that type.
We have to hack this check into the return type, since operator<<
only accepts two parameters.
More Info¶
Source Code¶
Get the source code for the has_member
class here: has_member.hpp
.
It is Apache licensed, so use it under those terms (just drop the header into your code).
There is also a demo program: has_member.cpp
.
It has been compiled and tested with g++ 4.6.2, but any compiler with the <type_traits>
library should be perfectly
fine with it (or you can change it to use Boost’s Type Traits library).