2013-06-30 11 views
9

Da una domanda precedente:Utilizzando un alias modello invece di un modello all'interno di un modello

Doing a static_assert that a template type is another template

Andy Prowl mi ha fornito questo codice che mi permette di static_assert che un tipo di modello è un altro tipo di modello:

template<template<typename...> class TT, typename... Ts> 
struct is_instantiation_of : public std::false_type { }; 

template<template<typename...> class TT, typename... Ts> 
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { }; 

template<typename T> 
struct foo {}; 

template<typename FooType> 
struct bar { 
    static_assert(is_instantiation_of<foo,FooType>::value, ""); //success 
}; 

int main(int,char**) 
{ 
    bar<foo<int>> b; //success 
    return 0; 
} 

Questo funziona benissimo.

Ma se cambio il codice come questo per utilizzare un alias di foo, le cose vanno male:

template<template<typename...> class TT, typename... Ts> 
struct is_instantiation_of : public std::false_type { }; 

template<template<typename...> class TT, typename... Ts> 
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { }; 

template<typename T> 
struct foo {}; 

//Added: alias for foo 
template<typename T> 
using foo_alt = foo<T>; 

template<typename FooType> 
struct bar { 
    //Changed: want to use foo_alt instead of foo here 
    static_assert(is_instantiation_of<foo_alt,FooType>::value, ""); //fail 
}; 

int main(int,char**) { 
    //both of these fail: 
    bar<foo<int>> b; 
    bar<foo_alt<int>> b2; 

    return 0; 
} 

può risolvere il problema?

+0

Hmm, sembra che "foo_alt' sia un * typedef-name * e non un * nome-modello * ... ma ciò influisce solo sulla' barra'; puoi mantenere 'main' così com'è. –

+0

Penso che potresti verificare se due tipi sono delle istanze dello stesso modello, anche questo funziona con i modelli di alias. – dyp

risposta

6

No, non può essere risolto (non senza modificare il design in modo significativo almeno). Il problema è che i nomi alias del modello non sono dedotti, come menzionato nel paragrafo 14.5.7/2 della norma C++ 11:

Quando un modello-ID fa riferimento alla specializzazione di un modello di alias, è equivalente al tipo associato ottenuto dalla sostituzione dei suoi argomenti modello per i parametri modello nell'id del tipo dell'alias modello . [Nota: Un nome di modello di alias è mai nota deduced.-end]

Il paragrafo fornisce anche un esempio:

[Esempio:

template<class T> struct Alloc {/.../}; 
template<class T> using Vec = vector<T, Alloc<T>>; 
Vec<int> v; // same as vector<int, Alloc<int>> v; 

... 

template<template<class> class TT> 
void f(TT<int>); 
f(v); // error: Vec not deduced       <=== Relevant 

... 

- fine esempio]

Nel tuo caso concreto, il problema è che quando si cerca di abbinare la specializzazione parziale, il compilatore non dedurrà che il tuo tipo è un'istanza di foo_alt (dal foo_alt è il nome di un modello di alias) e il modello primario viene scelto

Se si desidera utilizzare i modelli di alias, si dovrà rinunciare a un po 'di genericità e di creare un tipo di tratto specifico per foo:

#include <type_traits> 

template<typename T> 
struct foo {}; 

template<typename T> 
struct is_instantiation_of_foo : std::false_type { }; 

template<typename...Ts> 
struct is_instantiation_of_foo<foo<Ts...>> : std::true_type { }; 

cui si potrebbe quindi utilizzare in questo modo:

template<typename FooType> 
struct bar { 
    static_assert(is_instantiation_of_foo<FooType>::value, ""); //fail 
}; 

Ora, nessuna delle affermazioni del seguente programma scatterà:

template<typename T> 
using foo_alt = foo<T>; 

int main(int,char**) { 
    // None of these fail: 
    bar<foo<int>> b; 
    bar<foo_alt<int>> b2; 

    return 0; 
} 

Ecco un live example.

+0

Forse possiamo fare di meglio. La corrispondenza del modello sicuro di SFINAE' deduce il 'template'' M' in modo tale che il tipo passato sia 'M ' per alcuni 'Ts ...'. Quindi controlla 'std :: is_same , M >', anche SFINAE custodito nel caso in cui 'X ' sia mal formato, dove' X <> 'è il' modello' passato per corrispondere ... – Yakk

+0

@Yakk: Sei troppo geniale per me :) Non capisco cosa significhi "deduzione di SFINAE", e temo che avrei bisogno di fissare la soluzione qualche minuto prima di ottenerlo comunque;) –

+0

vedi la risposta di dyn: Ero meno che chiaro e scrivere codice su questa tastiera è difficile, intendevo "dedurre in un modo sicuro di SFINAE", quindi se non riuscisse a combaciare avrebbe semplicemente detto "falso". Dyn individuò il difetto che mi mancava: è troppo intelligente per me! – Yakk

1

Se si dispone di un modello di alias che fa non alterare i parametri di modello della classe di cui (come nel tuo esempio, ma solo rinomina il modello di cui), allora si può usare qualcosa di simile (non è il modo più elegante)

template < template<class...> class TT0, template<class...> class TT1, 
      class... Ts1 > 
struct is_from_same_template_helper 
{ 
    template < class T = TT0<Ts1...>, 
       class = typename std::enable_if< 
        std::is_same<TT0<Ts1...>, TT1<Ts1...>>::value 
       >::type 
      > 
    static std::true_type test(int); 

    template < class T = int > 
    static std::false_type test(...); 
}; 


template<template<class...> class, class> 
struct is_instantiation_of : public std::false_type { }; 

template<template<class...> class TT0, template<class...> class TT1, 
     class... Ts1> 
struct is_instantiation_of<TT0, TT1<Ts1...>> 
    : public decltype(is_from_same_template_helper<TT0, TT1, Ts1...> 
         ::template test<>(0)) 
{ }; 

Grazie a Yakk per aver ricordato che è necessario uno SFINAE-tipo di controllo al fine di lasciare solo il static_assert fail (versione precedente non è riuscito a causa di un errore di compilazione prima della static_assert).

Poiché solo le specializzazioni di modelli alias sono uguali alle specializzazioni del modello indicato; il modello alias stesso non è uguale al modello indicato.

Se il modello di alias modifica i parametri del modello, l'implementazione sopra può creare falsi negativi.


Come xy-problema, è possibile modificare l'implementazione per:

#include <type_traits> 


template < template <typename...> class TT0, template <typename...> class TT1 > 
struct is_same_template : public std::false_type { }; 

template < template <typename...> class TT > 
struct is_same_template < TT, TT > : public std::true_type { }; 


template < typename T0, typename T1 > 
struct is_from_same_template : public std::false_type { }; 

template < template <typename...> class TT0, template <typename...> class TT1, 
      typename... Ts0, typename... Ts1 > 
struct is_from_same_template < TT0<Ts0...>, TT1<Ts1...> > 
    : public is_same_template<TT0, TT1> { }; 


template<typename T> 
struct foo {}; 

//Added: alias for foo 
template<typename T> 
using foo_alt = foo<T>; 

template<typename FooType> 
struct bar { 
    //Changed: want to use foo_alt instead of foo here 
    static_assert(is_from_same_template<foo_alt<int>, FooType>::value, ""); 
}; 

int main(int,char**) { 
    //both of these succeed: 
    bar<foo<int>> b; 
    bar<foo_alt<int>> b2; 

    return 0; 
} 

Naturalmente, è necessario disporre di un'esemplificazione valida di foo_alt per questo approccio.

+0

Non riesco a capire quale sia lo scopo del parametro template 'TT1' del modello' is_from_same_template_helper'. – Constructor

+0

@Constructor Come ho "annunciato" nel mio commento precedente, c'è stato un bug nel codice (introdotto in base alla cronologia delle modifiche quando si passa da errore hard a SFINAE). È chiaro ora? – dyp

+0

Oh, mi dispiace, non ho notato il tuo commento. Adesso è chiaro, grazie. Come vedo, non puoi decidere quale parola chiave ti piace di più ('typename' o' class'). :-) – Constructor

Problemi correlati