8

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?

Juraj Blaho
  • 13,301
  • 7
  • 50
  • 96

4 Answers4

6

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) 
{

}
Anycorn
  • 50,217
  • 42
  • 167
  • 261
  • 1
    I prefer this one. In both cases you'll get an error if you use it with the wrong type, but with this version you can override based on different criteria to catch the other kinds. Based on the fact that `<<` is a popular operator, and you're doing this kind of thing, my select/case metafunction thingy might be of use. http://crazycpp.wordpress.com/2011/02/16/28/ – Edward Strange Feb 24 '11 at 18:23
  • Just to clear some little things out: this function template is suitable for Base class too, not only for derived classes. – Deepscorn Jun 20 '14 at 08:34
2

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'
}
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
2

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

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • +1. I liked the idea. Very Good. More concrete way to resolve the ambiguities. – Nawaz Feb 24 '11 at 18:34
  • Instead of `getDerived`, you can also write user-defined conversion function, right? – Nawaz Feb 24 '11 at 18:37
  • @Nawaz that wouldn't work too well. Given a `BaseOutputtable`, you would need to write `static_cast(derived)` to get a derived pointer or reference. And in that case, I believe it wouldn't even call the conversion function, but do the cast directly - since there is an inheritance relationship. So I cannot see a user-defined conversion function to be a useful replacement for the explicit member function call. – Johannes Schaub - litb Feb 24 '11 at 20:11
1

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> {};
Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • `static_assert` is of the form `static_assert(bool, const char*)`, that is, `static_assert(is_base_of::value, "Failed!");` – Puppy Feb 24 '11 at 18:17
  • 1
    @DeadMG: I'm giving C++03 solution. See my solution now. It's complete. – Nawaz Feb 24 '11 at 18:19
  • wont it assert false the first time you use any non-derived type? – Anycorn Feb 24 '11 at 18:21
  • @aaa: That is what it meant for. To make it work only for derived classes of `Base`. – Nawaz Feb 24 '11 at 18:22
  • That does not solve the problem. This operator<< will override the std::ostream operator<< for every type, not just the types derived from Base. It will just raise a static assert. – Juraj Blaho Feb 24 '11 at 18:22
  • but it will kill compilation as soon as anything matches the template. eg `int i; cout << i` – Anycorn Feb 24 '11 at 18:23
  • @Juraj: Did you even try? It will give compilation error, for other type! – Nawaz Feb 24 '11 at 18:23
  • @aaa: No. for `int`, there is a better match! – Nawaz Feb 24 '11 at 18:25
  • @Nawaz: the issue is that a compilation error is not SFINAE, therefore you cannot declare another `template ` for `T` which derive from `AnotherBase` because it'll clash at overload resolution. – Matthieu M. Feb 24 '11 at 18:30
  • @Juraj - The compiler is forced to use the closest match when there are multiple possible template resolutions. All that has to be done is to have something less generic than 'T' as the parameter type. You'd not be able to supply an alternative 'T' kind of overload or you'd get ambiguity errors. You'd still be able to provide other, more specific (including non-template) overloads though. – Edward Strange Feb 24 '11 at 18:31
  • @Nawaz: Yes I have tried that on g++ compiler and all types matched the template. Maybe I will have to try it again and check if I didn't make some miskate. – Juraj Blaho Feb 24 '11 at 18:31