6

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?

a06e
  • 18,594
  • 33
  • 93
  • 169
  • Related to [How to I restrict a template class to certain types?](http://stackoverflow.com/q/16976720/1708801) – Shafik Yaghmour Jul 23 '15 at 19:39
  • 1
    @ShafikYaghmour Note that it is not a duplicate. I want to restrict a *function* template, while the linked question refers to a *class* template. – a06e Jul 23 '15 at 19:41
  • @ShafikYaghmour Would you not have closed it if this was an exact duplicate and you did have an answer on the other one? – Barry Jul 23 '15 at 19:49
  • @Barry I don't think it is an exact duplicate ... as an aside I mentioned the only reason I would not use the dup hammer is if I had an answer on the proposed dup. – Shafik Yaghmour Jul 23 '15 at 19:51
  • @ShafikYaghmour Why not? Don't want to feel like you're gaming the system? – Barry Jul 23 '15 at 19:53
  • @Barry this [meta question](http://meta.stackoverflow.com/q/277126/1708801) made me a lot more conservative about how I use it. – Shafik Yaghmour Jul 23 '15 at 19:56
  • The key issue to select the best answer is: if someone tries to use your function with inappropriate T, do you want failure, or do you want other overloads to be considered? – Nir Friedman Jul 24 '15 at 02:46
  • @NirFriedman I don't want failure, I want other overloads to be considered. Should I edit the question? – a06e Jul 24 '15 at 12:50
  • @becko I think so, it will make your question more useful to future readers. If this is what you want, then you definitely want SFINAE as Barry's answer explains; static_assert will not serve your needs. If you are ok with failure, then static_assert is simpler and gives better error messages. – Nir Friedman Jul 24 '15 at 15:34

3 Answers3

13

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 Ts 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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • I don't find `std::enable_if_t`. Should it be `std::enable_if`? – a06e Jul 23 '15 at 20:01
  • 1
    `templateusing enable_if_t=typename std::enable_if::type;` is a one-line implementation of `enable_if_t`. Stick it in `namespace notstd`, and when your compiler updates, switch `notstd::enable_if_t` to `std::enable_if_t`. The alias is well worth the extra bit of boilerplate, the syntax of `enable_if` without it is annoying. – Yakk - Adam Nevraumont Jul 23 '15 at 20:02
  • This is what I want, thanks (+1). Do you mind explaining, at least on the surface, how this works? – a06e Jul 23 '15 at 20:14
  • @Yakk Also, I will be writing a lot of functions like `foo`, and all should match only if `T` inherits from the same base class. Is there a shorthand I can use, a `typedef` or something, to avoid spelling the whole `template – a06e Jul 23 '15 at 20:24
5

SFINAE based techniques such as the following;

template <typename T,
  typename Test = std::enable_if_t<std::is_base_of<Foo, T>::value>>
void foo(T arg);

Are good to remove the function from the overload list - which would be a general case.

If you wish to keep the function in the list, and if chosen as the best overload to fail if the type matches some criteria (such as a base requirement here), the static_assert can be used;

template <typename T>
void foo(T arg)
{
  static_assert(std::is_base_of<Foo, T>::value, "failed type check");
  // ...
}
Niall
  • 30,036
  • 10
  • 99
  • 142
4

In C++1z with concepts lite, you can do this:

template<class T>
requires std::is_base_of<Foo, T>{}()
void foo(T arg) {
}

under the current (experimental) implementation. Which is pretty clean and clear. There may be a way to do something like:

template<derived_from<Foo> T>
void foo(T arg) {
}

but I haven't worked it out. You can definitely do:

template<derived_from_foo T>
void foo(T arg){
}

where we have a custom concept called derived_from_foo that applies iff the type is derived from foo. What I don't know how to do is template concepts -- concepts generated from template type parameters.


In C++14, here are two methods. First, normal SFINAE:

template<class T,
  class=std::enable_if_t<std::is_base_of<Foo, T>{}>
>
void foo(T arg) {
}

here we create a template that deduces the type T from its argument. It then tries to deduce its second type argument from the first argument.

The second type argument has no name (hence class=), because we are only using it for a SFINAE test.

The test is enable_if_t< condition >. enable_if_t< condition > generates the type void if condition is true. If condition is false, it fails in "the immediate context", generating a substitution failure.

SFINAE is "Substitution failure is not an error" -- if your type T generates a failure in the "immediate context" of the function template signature, this doesn't generate a compile-time error, but instead results in the function template not being considered a valid overload in this case.

"Immediate context" is a technical term here, but basically it means the error has to be "early enough" to be caught. If it requires compiling bodies of functions to find the error, that is not in "the immediate context".

Now, this isn't the only way. I personally like hiding my SFINAE code behind a gloss of respectability. Below, I use tag dispatching to "hide" the failure somewhere else, instead of putting it right up front in the function signature:

template<class T>
struct tag {
  using type=T;
  constexpr tag(tag const&) = default;
  constexpr tag() = default;
  template<class U,
    class=std::enable_if_t<std::is_base_of<T,U>{}>
  >
  constexpr tag(tag<U>) {}
};

struct Base{};
struct Derived:Base{};

template<class T>
void foo( T t, tag<Base> = tag<T>{} ) {
}

here we create a tag dispatch type, and it allows conversion to base. tag lets us worth with types as values, and use more normal C++ operations on them (instead of template-like metaprogramming <>s all over the place).

We then give foo a second argument of type tag<Base>, then construct it with a tag<T>. This fails to compile if T is not a derived type from Base.

live example.

The nice thing about this solution is that the code that makes it not work seems more intuitive -- tag<Unrelated> cannot convert to tag<Base>. This does not, however, prevent the function from being considered for overload resolution, which can be a problem.

A way with less boiler plate is:

template<class T>
void foo( T t, Base*=(T*)0 ) {
}

where we use the fact that pointers can be converted iff there is a derivation relationship between them.


In C++11 (and without constexpr support), we first write a helper:

namespace notstd {
  template<bool b, class T=void>
  using enable_if_t=typename std::enable_if<b,T>::type;
}

then:

template<class T,
  class=notstd::enable_if_t<std::is_base_of<Foo, T>::value>
>
void foo(T arg) {
}

if you don't like the helper, we get this ugly extra:

template<class T,
  class=typename std::enable_if<std::is_base_of<Foo, T>::value>::type
>
void foo(T arg) {
}

the second C++14 technique above can also be translated to C++11.


You can write an alias that does the test if you want:

template<class U>
using base_test=notstd::enable_if_t<std::is_base_of<Base, U>::value>;

template<class T,
  class=base_test<T>
>
void foo(T arg) {
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • +1 Great answer, but please see my last comment to Barry's answer. Is there a shorthand to avoid writing the same template many times? I am using CLion and I think it doesn't fully understand C++14, so I am stuck with C++11. – a06e Jul 23 '15 at 20:27
  • @becko short-form alias added. Note however that the `tag` solution is pretty brief. – Yakk - Adam Nevraumont Jul 23 '15 at 20:32
  • "gloss of respectability", *snort*. your [excellent as usual] answers always crack me up. – Barry Jul 24 '15 at 15:07