dlaczego parametru szablonu, który jest wyraźnie podany, nie można „wywnioskować”

Nov 19 2020

Wychodząc z tego pytania: Używanie wartości wyliczenia w połączeniu z SFINAE

Próbowałem wdrożyć:

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

Zawodzi z:

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

Jak naprawić ten super prosty przykład? Lubię SFINAE :-)

Odpowiedzi

3 IlCapitano Nov 19 2020 at 12:48

Umieścić enable_ifw Foo„s liście argumentów szablonu:

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

demo .

2 dfrib Nov 19 2020 at 12:57

Jak mówi nam błąd, argumentów szablonów nie można wyprowadzić z częściowej specjalizacji. W naszym przykładzie próbowałeś umieścić konstrukcję SFINAE na liście parametrów szablonu specjalizacji, ale musisz przenieść ją na listę argumentów szablonu (klasy, która jest wyspecjalizowana) deklaracji specjalizacji.

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

Zastosowany do twojego przykładu (trochę wyczyszczony):

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

Zauważ, że nie musisz nazywać nieużywanego parametru szablonu typu w podstawowym szablonie szablonu klasy Foo.

1 Deduplicator Nov 19 2020 at 12:49

To prosty fakt, że argumentów szablonów w specjalizacjach szablonów nie można wywnioskować.

Nie żebyś tego potrzebowała.

Po prostu zmień specjalizację szablonu:

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

Chociaż oczywiście wynik std::enable_if_tbycia tutaj intzamiast voidczyni go nieco bezużytecznym.

Ponadto, jak komentowali inni, używanie pojęć lub przynajmniej requireszamiast dodatkowego argumentu-szablonu z szablonu podstawowego jest znacznie wygodniejsze.