13

Si consideri il codice:È legale di specializzarsi parzialmente classe interna template variadic con args dal modello variadic di una classe esterna

#include <iostream> 

template <class... Ts> 
struct outer { 
    template <class... ITs> 
    struct inner { 
     static constexpr bool value = false; 
    }; 

    template <class... ITs> 
    struct inner<Ts..., ITs...> { 
     static constexpr bool value = true; 
    }; 
}; 

int main() { 
    std::cout << outer<int, float, double>::inner<int, float, double, int>::value << std::endl; 
} 

il codice viene compilato con clangore ++ ma non con g ++ dove si produce un errore:

temp3.cc:11:11: error: parameter pack argument ‘Ts ...’ must be at the end of the template argument list

struct inner<Ts..., ITs...> { 
    ^

Come ho già stabilito here la specializzazione parziale della classe interna dovrebbe essere legittima.

Edit: Per completezza vale la pena di aggiungere che clang per il codice di cui sopra avverte che si potrebbe avere un problema con i suoi parametri dedurre ancora farlo senza problemi ...

+0

Non conosco esattamente le regole, ma quando ottengo un errore con un tipo dipendente, l'aggiunta di 'typename' o' template' prima di qualche volta aiuta. Prova 'struct inner Dani

+0

dopo l'aggiunta di 'typename' ottengo' temp3.cc:11:39: errore: l'argomento modello 1 non è valido' –

+0

Si noti che lo scenario richiesto può ancora essere implementato con qualche modello aggiuntivo metaprogrammazione ... http://coliru.stacked-crooked.com/a/0c6c643c8ff5809e (sì, so che non era la domanda, ma la sfida di implementarlo era inevitabile ...). –

risposta

8

Questo è un bug gcc . Questa è una specializzazione parziale perfettamente valida:

template <class... ITs> 
struct inner<Ts..., ITs...> { 
    static constexpr bool value = true; 
}; 

parametri template dedurre pacchi devono essere scorso, e ITs... soddisfa che. Ma Ts... non è un pacchetto che deve essere dedotto qui, è solo un pacchetto di parametri specifici.

Inoltre, gcc compila diverse formulazioni equivalenti:

template <class... Ts> 
struct X { 
    template <class... Us> 
    static void foo(Ts..., Us...) { } 
}; 

int main() { 
    X<int>::foo(1, 'c'); 
} 

e:

template <class... Us> 
struct A { }; 

template <class... Ts> 
struct X { 
    template <class... Us> 
    static void foo(A<Ts..., Us...>) { } 
}; 

int main() { 
    X<int>::foo(A<int, char>{}); 
} 

presentate equivalentemente ben formate per tuo esempio originale.

+0

Lo sospettavo ma non ero sicuro se non ci fosse qualche regola speciale per il contesto di specializzazione all'interno di una classe template. È un bug noto? –

+4

Sono d'accordo sul fatto che abbia senso che la specializzazione parziale sia valida, ma non sono sicuro che lo standard come scritto lo consenta. [14.5.5p8.5] dice * Se un argomento è un'espansione di pacchetto (14.5.3), sarà l'ultimo argomento nell'elenco di argomenti del template *. È un requisito specifico per le specializzazioni parziali, quindi non si applica ai tuoi esempi. Questo sembra più un problema standard di un bug del compilatore per me. – bogdan

+0

È necessario inviare una segnalazione di errore a 'gcc'. Avete fatto? – Destructor

0

possibile soluzione semplice ma efficace ispirato sulla risposta di Barry:

#include <iostream> 

template <class... Ts> 
struct pack { }; 

template <class... Ts> 
struct outer { 
    template <class IT> 
    struct inner { 
     static constexpr bool value = false; 
    }; 

    template <class... ITs> 
    struct inner<pack<Ts..., ITs...>> { 
     static constexpr bool value = true; 
    }; 
}; 

int main() { 
    std::cout << outer<int, float, double>::inner<pack<int, float, double, int>>::value << std::endl; 
} 

(Si produce ancora l'avviso in clang però)

1

Cavalcando la risposta di WF, mantenendo sempre lo stesso principale come nel domanda iniziale:

#include <iostream> 

template <class... Ts> 
struct pack { }; 

template <class... Ts> 
class outer { 
    template <class IT> 
    struct _inner { 
     static constexpr bool value = false; 
    }; 

    template <class... ITs> 
    struct _inner<pack<Ts..., ITs...>> { 
     static constexpr bool value = true; 
    }; 
public: 
    template <class... ITs> 
    struct inner { 
     static constexpr bool value = _inner<pack<ITs...>>::value; 
    };  
}; 

int main() { 
    std::cout << outer<int, float, double>::inner<int, float, double, int>::value 
      << std::endl; 
} 

Si produce ancora un avvertimento in clang, perché la versione specializzata di _inner non poteva dedurre la sua ... a parte l'elenco dei Ts ... , IT ... (in struct _inner<pack<Ts..., ITs...>>) - tuttavia il codice non richiede che gli IT siano dedotti separatamente dall'elenco di Ts ..., ITs ..., quindi dovrebbe essere ok.

In g ++ compila senza un avviso.

Codice: http://coliru.stacked-crooked.com/a/ae3b21dd847450b2

(Per una soluzione senza un avvertimento anche in clang: http://coliru.stacked-crooked.com/a/0c6c643c8ff5809e).