How to define a template function with the help of std::enable_if

Viewed 105

What I'm trying to do is to define a template function, which can only be specializd by the class, which inherits some classes.

For example, I have already had two class Base1 and Base2. I'm trying to define such a template function:

template<typename T>  // if (std::is_base_of<Base1, T>::value || std::is_base_of<Base2, T>::value)
std::ostream & operator<<(std::ostream &os, const T& t)
{
    // os << t.member1 << t.member2...;
    return os;
}

It seems that std::enable_if can help but I don't know how.

1 Answers

C++11

template <typename T, typename = typename std::enable_if<
                          std::is_base_of<Base1, T>::value ||
                          std::is_base_of<Base2, T>::value>::type>
std::ostream &operator<<(std::ostream &os, const T &t) {
  // os << t.member1 << t.member2...;
  return os;
}

or:

template <typename T, typename std::enable_if<
                          std::is_base_of<Base1, T>::value ||
                          std::is_base_of<Base2, T>::value>::type * = nullptr>
std::ostream &operator<<(std::ostream &os, const T &t) {
  // os << t.member1 << t.member2...;
  return os;
}

The latter approach can be useful when wanting to provide mutually exclusive SFINAE-constrained overloads that differ only in their SFINAE predicates. The former approach is not viable in that case as two template functions differing only in their default template arguments declare the same function template, as default template arguments are not part of a function template's signature.

C++14 (using the std::enable_if_t utility alias template)

template <typename T,
          typename = std::enable_if_t<std::is_base_of<Base1, T>::value ||
                                      std::is_base_of<Base2, T>::value>>
std::ostream &operator<<(std::ostream &os, const T &t) {
  // os << t.member1 << t.member2...;
  return os;
}

C++17 (using the _v utility variable templates)

template <typename T, typename = std::enable_if_t<std::is_base_of_v<Base1, T> ||
                                                  std::is_base_of_v<Base2, T>>>
std::ostream &operator<<(std::ostream &os, const T &t) {
  // os << t.member1 << t.member2...;
  return os;
}
Related