4
votes

Correspondance explicite des arguments de modèle de modèle

Considérez cette fonction:

template<template<class, class> class C, class T, class Alloc>
void foo(C<T, Alloc>& container) {
    std::cout << container.size() << std::endl;
}

La fonction foo () accepte un std :: vector , mais il accepte également std :: map en C ++ 17 car Compare et Allocator ont des valeurs par défaut. Remarque std :: map échoue dans C ++ 14.

Comment puis-je faire en sorte que foo () n'accepte que des modèles avec exactement 2 paramètres de modèle, donc ça échoue avec std :: map en C ++ 17?


12 commentaires

Pourquoi voulez-vous? Pourquoi le nombre de paramètres du modèle est-il significatif?


J'ai une surcharge de foo avec 4 paramètres de modèle pour correspondre à std :: map , mais foo (std :: map) est ambigu en c ++ 17.


Voulez-vous exclure la carte ou tout C<> avec plus de 2 arguments? Pourquoi voulez-vous faire tout cela de toute façon? Cela ressemble beaucoup à un problème XY


Notez que std :: vector peut avoir n'importe quel nombre d'arguments de modèle.


Je veux exclure tous les C<> avec plus de 2 arguments. C'est probablement un problème XY, mais pour faire court, il s'agit de mettre à jour le code tiers.


C doit-il avoir un membre size_t C :: size () const ?


Oui, j'attends un conteneur de std ou d'une classe ad hoc qui réplique leur interface dans une certaine mesure.


Mais pourquoi doit-il éviter la carte ?


@Walter plase voir le deuxième commentaire. J'ai déjà une version de foo pour map , donc cette autre fonction rend l'appel ambigu.


Peut-être simplement désactiver cette fonction particulière (més) jusqu'à ce que le noyau corrige l'ordre partiel.


L'implémentation de cela via le paramètre de modèle de modèle est problématique et totalement inutile. Ne le fais pas. Utilisez plutôt un argument de modèle unique et utilisez SFINAE pour vous spécialiser pour des conteneurs particuliers si vous en avez besoin. Ou mieux encore, écrivez foo () pour prendre des arguments d'itérateur - la méthode typique pour le code générique dans la STL.


Fait intéressant, le dernier clang ne compile pas ceci: godbolt.org/z/aln7cD Uniquement les derniers gcc et clang ( concepts expérimentaux) faire.


3 Réponses :


2
votes

Créez une surcharge de modèle de la fonction qui prend un conteneur avec trois éléments. Lorsque vous essayez d'utiliser un conteneur avec deux paramètres, cela fonctionnera, lorsque vous essayez d'utiliser quelque chose avec trois paramètres avec le troisième ayant une valeur par défaut (comme std :: map ), cela échouera (car soit on pourrait le surcharger).

template <class A, class B, class C> bar{};

notez que cela ne fonctionne pas si quelqu'un entre quelque chose comme

#include <map>
#include <vector>
#include <iostream>

template<template<class, class, class> class C, class Key, class T, class Alloc>
void foo(C<Key, T, Alloc>& container) {
    std::cout << "three arguments" << std::endl;
}

template<template<class, class> class C, class T, class Alloc>
void foo(C<T, Alloc>& container) {
    std::cout << "two arguments" << std::endl;
}



int main() {
    std::vector<int> v;
    std::map<int,int> m;

    foo(v);
    foo(m);
}

car cela ne correspondra que option à trois paramètres, donc ce ne sera pas ambigu


1 commentaires

Votre solution fonctionne avec g ++ mais ne se compile pas avec clang ++. Je ne sais pas qui a raison mais, pour travailler avec les deux compilateurs, il semble suffisant d'ajouter une classe ... dans la signature template-template pour la fonction à trois cas (je veux dire: template