specializzazione parziale di modelli di classe si basa su pattern matching. Al contrario, la personalizzazione dei modelli di funzione si basa su deduzione argomento modello e risoluzione sovraccarico.
A causa della classe gerarchia presente nel vostro problema, personalizzando comportamento sarebbe in linea di principio essere più conveniente attraverso template overloading di funzioni, perché in grado di prendere derivato-to-base conversioni in considerazione. La corrispondenza del modello utilizzata nella specializzazione del modello di classe parziale non offre la stessa flessibilità.
Tuttavia, dal C++ 11, è possibile eseguire la deduzione del tipo di reso in fase di compilazione. Ecco una soluzione che combina tag di dispacciamento, costruttori predefiniti e decltype
tipo deduzione: (! C++ 14 modalità non è richiesta)
#include <iostream>
// file foo_base.h:
struct foo_base
{
foo_base() = default;
};
foo_base faux(foo_base const&)
{
return foo_base{};
}
template<class T, class = decltype(faux(T{}))>
struct aux;
template<class T>
struct aux<T, foo_base>
{
enum { value = 1 };
};
// file foo.h:
template<typename T>
struct foo : foo_base
{
foo() = default;
};
// file bar.h:
template<typename T>
struct bar : foo<T>
{
bar() = default;
};
template<class T>
bar<T> faux(bar<T> const&)
{
return bar<T>{};
}
template<class T, class U>
struct aux<T, bar<U>>
{
enum { value = 2 };
};
// file meow.h
template<class T>
struct meow : bar<T>
{
meow() = default;
};
int main()
{
std::cout << aux<foo_base>::value; // 1
std::cout << aux<foo<int>>::value; // 1
std::cout << aux<bar<int>>::value; // 2
std::cout << aux<meow<int>>::value; // 2
}
Live Example che funziona sia con g ++ e clang in C++ 11 modalità .
La funzione di constexpr
faux()
è sovraccarico per foo_bar
e come un modello di funzione per bar<T>
. Qualsiasi argomento la cui classe è derivata da foo_base
ma non da bar<T>
selezionerà il precedente sovraccarico e qualsiasi cosa derivata da bar<T>
selezionerà il secondo sovraccarico. Questo meccanismo è lo stesso di ad es. nella Libreria standard in cui le categorie iteratore vengono utilizzate per la spedizione di tag diverse implementazioni di std::advance()
ad es.
Per utilizzare questo meccanismo di selezione durante la specializzazione parziale del modello di classe aux
, sono necessari altri due ingredienti. Innanzitutto, tutte le classi devono avere un costruttore predefinito . In secondo luogo, decltype()
viene applicato a tale espressione faux(T{})
a dedurre il tipo di reso.
NOTA: non è necessario che faux()
è constexpr
o che uno dei costruttori di default sono constexpr
, perché decltype()
sarà in realtà non valutare la chiamata di funzione, ma solo dedurre il tipo restituito .
Il modello di classe principale aux
ha un argomento modello predefinito:
template<class T, class = decltype(faux(T{}))>
struct aux;
parzialmente specializzata il secondo argomento su foo_base
permette di fornire il comportamento per qualsiasi classe che deriva da foo_base
:
template<class T>
struct aux<T, foo_base>
{ // custom behavior for anything derived from foo_bar };
La seconda specializzazione parziale corrisponde a qualsiasi classe derivata da qualsiasi istanza di modello bar<U>
template<class T, class U>
struct aux<T, bar<U>>
{ // custom behavior for anything derived from bar<U> for some U }
NOTA: l'inconveniente principale è che si potrebbe essere necessario fornire un costruttore di default per tutte le classi nella gerarchia. Questo può o non può essere un ostacolo che puoi superare. La maggior parte delle classi ha già un costruttore predefinito, ma alcuni potrebbero non farlo. In questo senso, questa soluzione è invadente (vale a dire che non può essere fissata al di sopra del codice esistente, ma richiede la modifica di quel codice).
Come viene definito 'is_foo'? Può fornire un [SSCCE] (http://sscce.org)? – Praetorian
Come si desidera risolvere l'ambiguità? – Casey
@Praetorian vedere le modifiche. 'is_foo> :: value' è' true' (altrimenti non c'era ambiguità) –
Walter