templates - Especialización de plantilla parcial de C++ en combinación con std:: is_base_of y std:: enable_if
c++11 specialization (3)
Considera esto:
void print(const Printable& value) {
cout << value << endl;
}
void print(const Serializable& value) {
cout << value << endl;
}
Naturalmente, tendrá el operator<<
apropiado operator<<
llamando a una función virtual en el operando del lado derecho, que haría la impresión real.
Digamos que tengo dos clases: Serializable
e Printable
.
Por lo tanto, una función de plantilla simple que acepta todas las clases derivadas de Printable
podría parecerse a:
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;
}
Sin embargo, si quiero que acepte también todas las clases derivadas de Serializable
mientras todavía tengo control sobre el cuerpo de la función, esto obviamente no funcionará:
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 ...
Así que pensé que las soluciones restantes para este problema son las especializaciones de plantillas.
Pero no puedo entender cómo puedo especializar una plantilla en combinación con std::is_base_of
y std::enable_if
.
Espero que alguien esté dispuesto a ayudarme!
Pruebe un operador lógico:
std::enable_if<std::is_base_of<Serializable, T>::value ||
std::is_base_of<Printable, T>::value>::type
Puedes escribir fácilmente una plantilla variad como:
is_base_of_any<T, Printable, Serialiable, Googlable, Foobarable>::value
Por ejemplo:
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>
{ };
Si quieres diferentes implementaciones:
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>());
}
La última sobrecarga (la que se enfrenta al usuario) probablemente debería estar dotada de la enable_if
anterior para no crear demasiados candidatos de sobrecarga.
Probablemente también puede hacer una template <typename ...Bases>
con una etiqueta como:
tag_type<std::is_base_of<Bases, T>::value...>
Un poco menos de maquinaria que la respuesta de Kerrek, pero me temo que no más legible:
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";
}