问题
Let's say I have a two classes: Serializable
and Printable
.
So a simple template function which accepts all derived classes of Printable
could look like:
template <class T, class B = Printable, class = typename std::enable_if<std::is_base_of<B, T>::value>::type>
void print(T value) {
cout << value << endl;
}
However, if I want it to accept also all derived classes of Serializable
while I still have control over the function body, this would obviously not work:
template <class T, class B = Printable, class = typename std::enable_if<std::is_base_of<B, T>::value>::type>
void print(T value) {
cout << value << endl;
}
template <class T, class B = Serializable, class = typename std::enable_if<std::is_base_of<B, T>::value>::type>
void print(T value) {
cout << value << endl;
}
// Error: Redefinition of ...
So I figured the remaining solutions for this problem are template specializations.
But I just can't figure out, how I can specialize a template in combination with std::is_base_of
and std::enable_if
.
I hope someone is willing to help me!
回答1:
Try a logical operator:
std::enable_if<std::is_base_of<Serializable, T>::value ||
std::is_base_of<Printable, T>::value>::type
You can easily write a variadic template like:
is_base_of_any<T, Printable, Serialiable, Googlable, Foobarable>::value
For example:
template <typename T, typename ...> struct is_base_of_any : std::true_type {};
template <typename T, typename Head, typename ...Rest>
struct is_base_of_any<T, Head, Rest...>
: std::integral_constant<bool, std::is_base_of<T, Head>::value ||
is_base_of_any<T, Rest...>::value>
{ };
If you want different implementations:
template <bool...> struct tag_type {};
template <typename T>
void foo(T, tag_type<true, false>) { } // for Printable
template <typename T>
void foo(T, tag_type<false, true>) { } // for Serializable
template <typename T>
void foo(T x)
{
foo(x, tag_type<std::is_base_of<Printable, T>::value,
std::is_base_of<Serializable, T>::value>());
}
The last overload (the "user-facing" one) should probably be endowed with the above enable_if
to not create overly many overload candidates.
You can probably also make a variadic template <typename ...Bases>
with a tag like:
tag_type<std::is_base_of<Bases, T>::value...>
回答2:
A little less machinery than Kerrek's answer, but I'm afraid no more readable:
template <class T, typename std::enable_if<std::is_base_of<Printable, T>::value>::type* = nullptr>
void print(const T& value) {
std::cout << "printable(" << &value << ")\n";
}
template <class T, typename std::enable_if<std::is_base_of<Serializable, T>::value>::type* = nullptr>
void print(const T& value) {
std::cout << "serializable(" << &value << ")\n";
}
See it live at ideone.
回答3:
Consider this:
void print(const Printable& value) {
cout << value << endl;
}
void print(const Serializable& value) {
cout << value << endl;
}
Naturally you will have the appropriate operator<<
calling a virtual function in the right hand side operand, which would do the actual printing.
来源:https://stackoverflow.com/questions/17200755/c-partial-template-specialization-in-combination-with-stdis-base-of-and-std