Parameter syntax extending two classes

In Java, it can declare that a parameter implements multiple interfaces. You must use the 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 ICloseable

(it's just polymorphism):

void closeThis(ICloseable&);

      

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

, and from IAppendable

?

+3


source to share


2 answers


This is how you write it using only standard features:

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 would suggest creating a more concise character to test them all in 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();
}

      

+5


source


@ Quentin's excellent answer prompted me to write a generic variational pattern inherits

. This 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 of validation, and the remaining parameters are the types that the first type should inherit.

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 any type T

passed as an argument foo

must inherit from A

, B

and C

.

Live on coliru

+1


source







All Articles