web-dev-qa-db-fra.com

Spécialisation de modèle partiel C ++ en combinaison avec std :: is_base_of et std :: enable_if

Disons que j'ai deux classes: Serializable et Printable.

Ainsi, une fonction de modèle simple qui accepte toutes les classes dérivées de Printable pourrait ressembler à:

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;
}

Cependant, si je veux qu'il accepte également toutes les classes dérivées de Serializable alors que j'ai toujours le contrôle sur le corps de la fonction, cela ne fonctionnera évidemment pas:

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 ...

J'ai donc pensé que les solutions restantes pour ce problème étaient des spécialisations de modèle.

Mais je n'arrive pas à comprendre comment je peux spécialiser un modèle en combinaison avec std::is_base_of et std::enable_if.

J'espère que quelqu'un est prêt à m'aider!

28
Tim

Essayez un opérateur logique:

std::enable_if<std::is_base_of<Serializable, T>::value ||
               std::is_base_of<Printable, T>::value>::type

Vous pouvez facilement écrire un modèle variadic comme:

is_base_of_any<T, Printable, Serialiable, Googlable, Foobarable>::value

Par exemple:

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 vous souhaitez différentes implémentations:

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 dernière surcharge (celle "face à l'utilisateur") devrait probablement être dotée de enable_if pour ne pas créer trop de candidats en surcharge.

Vous pouvez probablement aussi faire un variadic template <typename ...Bases> avec une balise comme:

tag_type<std::is_base_of<Bases, T>::value...>
22
Kerrek SB

Un peu moins de machinerie que la réponse de Kerrek, mais je crains pas plus lisible:

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";
}

Voir vivre chez ideone .

21
Casey

Considère ceci:

void print(const Printable& value) {
    cout << value << endl;
}

void print(const Serializable& value) {
    cout << value << endl;
}

Naturellement, vous disposerez du operator<< appeler une fonction virtuelle dans l'opérande de droite, qui ferait l'impression proprement dite.

2
n.m.