2015-03-04 34 views
11

In primo luogo, lasciate che vi presenti una soluzione parziale:Come confrontare un modello di modello con un'istanza di modello?

template <template <class...> class, 
     typename ...> 
struct is_tbase_of: 
    std::false_type 
{ }; 

template <template <class...> class Type, 
      typename ...Args> 
struct is_tbase_of<Type, Type<Args...>>: 
    std::true_type 
{ }; 

Nei casi comuni, funziona:

is_tbase_of<std::vector, std::is_integral<int>>::value; // false 
is_tbase_of<std::vector, std::vector<int>>::value;  // true 

Ma, non funziona su un modello modello «meta-ritornato», per esempio:

template <template <class ...> class T> 
struct quote 
{ 
    template <typename ...U> 
    using type = T<U...>; 
}; 

using QVec = quote<std::vector>; 
is_tbase_of<QVec::template type, std::vector<int>>::value; // false... 

ho provato un sacco di cose, cercando di ottenere il secondo argomenti tipo di modello (per confrontare il tipo di specializzazione citato), ma a quanto pare non riesco a farli lavorare. Anche la specializzazione di is_tbase_of per quote (che sarebbe un'opzione meno generica ma sufficiente) sembra mandarmi agli angoli neri del modello di corrispondenza dei modelli.

risposta

4

Si può verificare se è possibile modificare U<Args...>-T<Args...> e quindi verificare se il risultato rimane lo stesso:

#include <type_traits> 
#include <vector> 

struct is_tbase_of_impl 
{ 
    struct err {}; 

    template <template <class...> class T, class U> 
    static err test(U*); 

    template <template <class...> class T, template <class...> class U, class... Args> 
    static T<Args...> test(U<Args...>*); 
}; 

template <template <class...> class T, class U> 
using is_tbase_of 
    = typename std::is_same< decltype(is_tbase_of_impl::test<T>((U*)0)), U >::type; 

template <template <class...> class T> 
struct quote 
{ 
    template <class... U> 
    using type = T<U...>; 
}; 

using QVec = quote<std::vector>; 

template <class...> struct S {}; 

static_assert(!is_tbase_of< std::vector, std::is_integral<int> >::value, ""); 
static_assert( is_tbase_of< std::vector, std::vector<int>  >::value, ""); 
static_assert( is_tbase_of< QVec::type, std::vector<int>  >::value, ""); 
static_assert(!is_tbase_of< std::vector, S<int, int, int>  >::value, ""); 

int main() 
{ 
} 
+1

Clever. Tuttavia, [c'è un piccolo buco] (http://coliru.stacked-crooked.com/a/c6ef3ae1217de2c6). Molto piccolo, non vale la pena di pensarci, ma guarda quell'ultima affermazione. Divertente se gli argomenti di 'S' sono gli stessi di' S2_', 'static_assert' fallisce! – Yakk

+0

Molto bello :-) Meno sorprendente, ma questo 'modello usando S2 = void;' fallirà sempre ... – Alex

+0

Penso che questo non possa generare un falso positivo, comunque. Ecco una domanda: la tua soluzione ha mai avuto una risposta diversa da [mia] (http://stackoverflow.com/a/28865203/1774667)? E se sì, chi è più corretto? (Penso che la risposta sia "no") – Yakk

2

Se specializzata is_tbase_of per quote è sufficiente, questo dovrebbe funzionare:

template <template <class...> class Type, 
      typename ...Args> 
struct is_tbase_of<quote<Type>::template type, Type<Args...>>: 
    std::true_type 
{ }; 
+0

[Demo] (https://ideone.com/2NxCT1) – TartanLlama

3

Il motivo per cui la tua implementazione originale non funziona è che, anche se QVec::type<Args...> è lo stesso tipo di std:vector<Args...>, QVec::type non è lo stesso modello come std::vector, quindi non corrisponde alla specializzazione parziale.

questo può essere visto con un esempio più semplice:

template <template <typename...> class> struct test { 
    static const bool value = false; 
}; 

template <> 
struct test<std::vector> { 
    static const bool value = true; 
}; 

test<std::vector>::value; // true 
test<QVec::type>::value; // false 

Ecco un approccio che funziona quasi:

template <template <class...> class Type1, 
      template <class...> class Type2, 
      typename... Args> 
struct is_tbase_of<Type1, Type2<Args...>>: 
    std::is_same<Type1<Args...>,Type2<Args...>> 
{ 
}; 

Tuttavia, come notato da @alex, questo non gestisce il caso dove gli argomenti del secondo modello non sono compatibili con il primo modello. Questo può essere risolto utilizzando enable_if:

template <template <class...> class, typename, typename=void> 
struct is_tbase_of : std::false_type { }; 

template <template <class...> class Type1, 
      template <class...> class Type2, 
      typename... Args> 
struct is_tbase_of<Type1, Type2<Args...>, 
    typename std::enable_if< 
    std::is_same<Type1<Args...>,Type2<Args...>>::value 
    >::type> 
    : std::true_type 
{ 
}; 
+0

Ok ma funziona solo se 'Tipo1 ' è valido. Altrimenti questo si traduce in un errore del compilatore. – Alex

+0

Se solo SFINAE ha funzionato per la selezione della specializzazione del modello parziale e – Joe

+0

@Joe come non funziona per la specializzazione del modello parziale? Il problema sopra è che la parte dopo ':' non è soggetta a SFINAE, dove avviene la sostituzione. – Yakk

4

questo è un tentativo di risolvere il problema con diretta metaprogramming struttura di modelli e SFINAE.

Il piano è di 2 volte. Innanzitutto, una classe di caratteri che prende un modello e un pacchetto di argomenti e risponde "è legale applicare il pacchetto di argomenti al modello". Questa è una struttura sorprendentemente utile: ad esempio, dato un amichevole di SFINAE result_of_t<F(Args...)>, è possibile scrivere can_invoke<F(Args...)> in una riga.

In secondo luogo, scriviamo is_template_instance_of. L'obiettivo qui è prendere un tipo T<Args...> e un modello Z<?...> e vedere se lo Z<Args...> è lo stesso tipo di T<Args...>. Utilizziamo la classe di caratteri can_apply di cui sopra per prevenire la sostituzione illegale, quindi eseguire un semplice test is_same.

La soluzione genera alcuni falsi positivi e negativi, a seconda di come la si guarda. Fondamentalmente se il modello Z<?...> con cui ci stiamo confrontando è un modello alias che non è un alias diretto, non funzionerà nel modo previsto.Se è un alias diretto, starai bene.

Senza ulteriori indugi, ecco l'implementazione.

In primo luogo, i tipi Boilerplate pack:

template<class...>struct types {using type=types;}; 

In C++ 1Z ha void_t, ho reimplementato qui:

template<class...>struct voider{using type=void;}; 
template<class...Ts>using void_t=typename voider<Ts...>::type; 

dato Z<?...> e types<Ts...>, controllare se Z<Ts...> è valido:

template<template<class...>class Z, class types, class=void> 
struct can_apply : std::false_type {}; 
template<template<class...>class Z, class...Ts> 
struct can_apply<Z, types<Ts...>, void_t<Z<Ts...>>> : std::true_type {}; 

Ora, un GU GuINAE Test TISCALI OTTIENE:

template<template<class...>class Z, class T, class=void> 
struct is_template_instance_of : std::false_type {}; 

template<template<class...>class Z, template<class...>class Y, class... Ts> 
struct is_template_instance_of< 
    Z, Y<Ts...>, 
    std::enable_if_t<can_apply< Z, types<Ts...> >{}> 
> : std::is_same< Z<Ts...>, Y<Ts...> > {}; 

live example

Problemi correlati