pourquoi le paramètre de modèle qui est explicitement donné ne peut pas être «déduit»

Nov 19 2020

Venant de cette question: Utilisation des valeurs d'énumération en combinaison avec SFINAE

J'ai essayé de mettre en œuvre:

enum Specifier
{
    One,
    Two,
    Three
};

template <Specifier, typename UNUSED=void>
struct Foo 
{
        void Bar(){ std::cout << "Bar default" << std::endl;}
};

template <Specifier s , typename std::enable_if<s == Specifier::Two || s == Specifier::One, int>::type>
struct Foo<s>
{
    void Bar(){ std::cout << "Bar Two" << std::endl; }
};


int main()
{
   Foo< One >().Bar();
   Foo< Two >().Bar();
}

Échoue avec:

> main.cpp:130:8: error: template parameters not deducible in partial specialization:
  130 | struct Foo<s>
      |        ^~~~~~
   main.cpp:130:8: note:         '<anonymous>'

Comment corriger cet exemple super simple? J'aime SFINAE :-)

Réponses

3 IlCapitano Nov 19 2020 at 12:48

Mettez le enable_ifdans Foola liste des arguments de modèle de »:

template <Specifier s>
struct Foo<s, typename std::enable_if<s == Specifier::Two || s == Specifier::One, void>::type>
//                                           same as the default type used before ^^^^

démo .

2 dfrib Nov 19 2020 at 12:57

Comme l'erreur nous l'indique, les arguments de modèle ne sont pas déductibles en spécialisation partielle. Dans votre exemple, vous avez essayé de placer la construction SFINAE dans la liste de paramètres de modèle de la spécialisation, mais vous devez la déplacer vers la liste d' arguments de modèle (de la classe en cours de spécialisation) de la déclaration de spécialisation.

template <Specifier S>
struct Foo<S, std::enable_if_t<(S == Specifier::Two) || (S == Specifier::One)>>

Appliqué à votre exemple (nettoyé un peu):

#include <iostream>
#include <type_traits>

enum class Specifier {
    One,
    Two,
    Three
};

template <Specifier, typename = void>
struct Foo {
    static void bar() { std::cout << "bar default\n"; }
};

template <Specifier S>
struct Foo<S, std::enable_if_t<(S == Specifier::Two) || (S == Specifier::One)>> {
    static void bar() { std::cout << "bar One or Two\n"; }
};

int main() {
    Foo<Specifier::One>::bar();    // bar One or Two
    Foo<Specifier::Two>::bar();    // bar One or Two
    Foo<Specifier::Three>::bar();  // bar default
}

Notez que vous n'avez pas besoin de nommer le paramètre de modèle de type non utilisé dans le modèle principal du modèle de classe Foo.

1 Deduplicator Nov 19 2020 at 12:49

C'est un fait simple que les arguments de modèle dans les spécialisations de modèle ne sont pas déductibles.

Non pas que vous en ayez besoin.

Changez simplement la spécialisation du modèle:

template <Specifier s>
struct Foo<s, std::enable_if_t<s == Specifier::Two || s == Specifier::One, int>>

Bien sûr, le résultat d' std::enable_if_têtre ici intau lieu de le voidrend quelque peu inutile.

De plus, comme d'autres l'ont commenté, utiliser des concepts ou du moins requiresau lieu de l'argument modèle supplémentaire du modèle principal est beaucoup plus pratique.