3

In Java, it's possible to declare that a parameter implements multiple interfaces. You have to use generics syntax, but you can:

public <T extends Appendable & Closeable> void spew(T t) {
    t.append("Bleah!\n");
    if (timeToClose())
        t.close();
}

In C++, a common pattern is to use classes containing only pure virtual functions as interfaces:

class IAppendable {
public:
    virtual void append(const std::string&) = 0;
};

class ICloseable {
public:
    virtual void close() = 0;
};

And it's trivial to write a function that takes an ICloseable (this is just polymorphism):

void closeThis(ICloseable&);

But what is the signature of a function that takes a parameter which, as in the Java example, inherits from both ICloseable and IAppendable?

George Hilliard
  • 15,402
  • 9
  • 58
  • 96
  • 1
    Is the question: how to implement a class that inherets from both `IAppendable` and `ICloseable`? Or: how to create a templated class/function/etc. whose `typename T` parameter must inheret both `IAppendable` and `ICloseable`? – EyasSH Jun 23 '15 at 21:55
  • The latter (I edited for clarity). Implementing a class implementing both is trivial: `class A : public IAppendable, public ICloseable {};` – George Hilliard Jun 23 '15 at 21:56

2 Answers2

5

Here is how you'd write it using only standard facilities :

template <class T>
std::enable_if_t<
    std::is_base_of<IAppendable, T>{} && std::is_base_of<ICloseable, T>{},
    void
> closeThis(T &t) {
    t.append("end");
    t.close();
}

Live on Coliru

If there were more base classes, I'd advise crafting a more concise type trait to check them all in the enable_if :

constexpr bool allTrue() {
    return true;
}

template <class... Bools>
constexpr bool allTrue(bool b1, Bools... bools) {
    return b1 && allTrue(bools...);
}

template <class T, class... Bases>
struct all_bases {
    static constexpr bool value = allTrue(std::is_base_of<Bases, T>{}...);

    constexpr operator bool () const {
        return value;
    }
};

template <class T>
std::enable_if_t<
    all_bases<T, IAppendable, ICloseable>{},
    void
> closeThis(T &t) {
    t.append("end");
    t.close();
}
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Interesting, thanks! Is using SFINAE on the return type the only way to do this? (I get the feeling the error message won't be really related to the actual programmer error.) – George Hilliard Jun 23 '15 at 22:17
  • 1
    @thirtythreeforty I think concepts, when they finally make it into the language, will help defining a much clearer syntax for these requirements. For now, this is the idiomatic way, overload-friendly and all. Uncommenting the `One` line triggers an error message containing `candidate: template std::enable_if_t{}, void> closeThis(T&)`. Not ideal, but not that bad for a template error ;) – Quentin Jun 23 '15 at 22:23
  • I'm not sure `is_base_of` is the right check, since it also detects ambiguous and inaccessible bases. (And of course, if we are using templates, I don't see why we need those base classes at all.) – T.C. Jun 23 '15 at 22:34
  • @T.C. I thought about `is_convertible`, but that covers less useful cases (left-hand side of an assignment) and more obscure ones (privately inheriting from an interface ??). Restricting the function to a set of interfaces essentially disables duck typing, which *could* be too liberal depending on the situation. – Quentin Jun 23 '15 at 22:44
  • I wrote a variadic implementation of your `all_bases`, see my answer. Lemme know if it can be improved. – George Hilliard Jun 24 '15 at 00:51
1

@Quentin's excellent answer prompted me to write a generalized, variadicinherits template. It allows you to easily specify an arbitrary number of base classes.

#include <type_traits>

template<class... T> struct inherits :
    std::true_type
{};

template<class T, class Base1, class... Bases>
struct inherits<T, Base1, Bases...> :
    std::conditional_t< std::is_base_of<Base1, T>{},
        inherits<T, Bases...>,
        std::false_type
    >
{};

The first template parameter is the type to check, and the remaining parameters are the types that the first type must inherit from.

For example,

class A {};
class B {};
class C {};

template<class T>
std::enable_if_t<
    inherits<T, A, B, C>{},
    void
> foo(const T& t)
{
    // ...
}

Here, whatever type T is passed as an argument to foo must inherit from A, B, and C.

Live on Coliru

George Hilliard
  • 15,402
  • 9
  • 58
  • 96