Reputation: 15952
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
?
Upvotes: 3
Views: 95
Reputation: 63144
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();
}
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();
}
Upvotes: 5
Reputation: 15952
@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
.
Upvotes: 1