Juraj Blaho
Juraj Blaho

Reputation: 13451

Define template function only for classes derived from a single base

I have a base class Base, that a lot of other classes will be derived from. I would like to define:

template<typename Derived>
ostream &operator<< (ostream &o, Derived &derived) {
}

But only for classes derived from Base. I need all previously defined operator<< to be used for other types. How to do that? Is that possible?

I cannot create ostream &operator<< (ostream &o, Base &base), because I need the exact type to be used in some type traits. Is there any way to "push" the derived type while passing the value as a base type?

Upvotes: 8

Views: 5009

Answers (4)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507005

Another option is to derive from a marker template

struct Base { /* ... */ };

template<typename T>
struct BaseOutputtable {
  T *getDerived() { 
    return static_cast<T*>(this);
  }

 T const *getDerived() const { 
    return static_cast<T const*>(this);
  }

protected:
  ~BaseOutputtable() { }
};

Then derive them from both

struct MyDerived : Base, BaseOutputtable<MyDerived> {
  /* ... */
};

Now you can write it as

template<typename Derived>
ostream &operator<< (ostream &o, BaseOutputtable<Derived> &derived) {
  /* ... */
}

The advantage of this is its simplicity. And if Base is already templated it's even more useful (I take it that this is not the case for your code).

Upvotes: 2

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361482

You can use is_base_of class template to ensure that only derived classes of Base can invoke operator<<:

template<typename Derived>
ostream &operator<< (ostream &o, Derived &derived) 
{
         static_assert<is_base_of<Base, Derived>::value>();
}

You can find the definition of is_base_of in another topic at stackoverflow itself: click here

And here is the definition of static_assert:

template<bool> struct static_assert;

template<> struct static_assert<true> {};

Upvotes: 1

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136276

You can use type traits and SFINAE to let only classes derived from Base into your function:

#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_base_and_derived.hpp>

struct Base {};

template<typename Derived>
typename boost::enable_if<boost::is_base_and_derived<Base, Derived>, std::ostream&>::type
operator<<(std::ostream& o, Derived& derived);

struct A : Base {};
struct B : Base {};
struct C {};

int main()
{
    A a;
    B b;
    C c;
    std::cout << a << '\n'; // compiles
    std::cout << b << '\n'; // compiles
    std::cout << c << '\n'; // error: no match for 'operator<<' in 'std::cout << c'
}

Upvotes: 2

Anycorn
Anycorn

Reputation: 51475

http://www.boost.org/doc/libs/1_46_0/libs/utility/enable_if.html

http://www.boost.org/doc/libs/1_42_0/libs/type_traits/doc/html/boost_typetraits/reference/is_base_of.html

template<typename Derived>
typename enable_if<is_base_of<Base, Derived>, ostream&>::type
operator<< (ostream &o, Derived &derived) 
{

}

Upvotes: 6

Related Questions