I want to define a function template:
template<typename T>
void foo(T arg)
But I want T
to match only certain types. Specifically, T
should derive (maybe through multiple inheritance) form a certain base class. Otherwise this template shouldn't be included in the overload set.
How can I do this?
Use SFINAE with std::is_base_of
:
template <typename T,
typename = std::enable_if_t<
std::is_base_of<Foo, T>::value
>>
void foo(T arg);
That will only include foo
in the overload set if T
inherits from Foo
. Note that this includes ambiguous and inaccessible bases as well. If you want a solution that only allows for T
s that inherit publicly and unambiguously from Foo
, then you can instead use std::is_convertible
:
template <typename T,
typename = std::enable_if_t<
std::is_convertible<T*, Foo*>::value
>>
void foo(T arg);
Note the reversal of arguments.
Regardless of which form you pick, it can be aliased for brevity:
template <typename T>
using enable_if_foo = std::enable_if_t<std::is_base_of<Foo, T>::value>;
template <typename T,
typename = enable_if_foo<T>>
void foo(T arg);
This works because std::enable_if
has a nested type named type
if and only if the boolean passed in is true
. So if std::is_base_of<Foo, T>::value
is true
, enable_if_t
gets instantiated to void
, as if we had written:
template <typename T,
typename = void>
void foo(T arg);
But, if T
does not inherit from Foo
, then the type trait will evaluate as false
, and std::enable_if_t<false>
is a substitution failure - there is no typename enable_if<false>::type
. You might expect this to a compile error, but substitution failure is not an error (sfinae). It's just a template deduction failure. So the effect is that foo<T>
is simply removed from the set of viable overload candidates in this case, no different from any other template deduction failure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With