44

Supponiamo di voler scrivere una funzione generica void f<T>(), che fa una cosa se T è un tipo POD e un'altra cosa se T non è POD (o qualsiasi altro predicato arbitrario).Invio di tag rispetto a metodi statici su classi parzialmente specializzate

Un modo per raggiungere questo obiettivo sarebbe quello di utilizzare un modello di tag-spedizione come la libreria standard fa con le categorie di iteratori:

template <bool> struct podness {}; 
typedef podness<true> pod_tag; 
typedef podness<false> non_pod_tag; 

template <typename T> void f2(T, pod_tag) { /* POD */ } 
template <typename T> void f2(T, non_pod_tag) { /* non-POD */ } 

template <typename T> 
void f(T x) 
{ 
    // Dispatch to f2 based on tag. 
    f2(x, podness<std::is_pod<T>::value>()); 
} 

Un'alternativa sarebbe quella di utilizzare la funzione di membro static di tipi parzialmente specializzati:

template <typename T, bool> struct f2; 

template <typename T> 
struct f2<T, true> { static void f(T) { /* POD */ } }; 

template <typename T> 
struct f2<T, false> { static void f(T) { /* non-POD */ } }; 

template <typename T> 
void f(T x) 
{ 
    // Select the correct partially specialised type. 
    f2<T, std::is_pod<T>::value>::f(x); 
} 

Quali sono i pro e i contro dell'utilizzo di un metodo rispetto all'altro? Quale raccomanderesti?

+3

Qualunque cosa faccia galleggiare la vostra barca. Trovo la seconda versione più "typetraity" e accattivante, perché c'è meno codice ausiliario e meno concetti nascosti. Inoltre aggiungerei l'inoltro per l'argomento! –

risposta

15

Vorrei tag spedizione perché:

  • semplice per estendere con nuovi tag
  • Eredità facile da usare (example)
  • It è una tecnica abbastanza comune nella programmazione generica

Mi sembra complicato aggiungere terza variante nel secondo esempio. Quando si desidera aggiungere, ad esempio il tipo non POD-of-POD, è necessario sostituire bool in template <typename T, bool> struct f2; con qualcosa di altro (int se si desidera =)) e sostituire tutti struct f2<T, bool-value> con struct f2<T, another-type-value>. Quindi per me la seconda variante sembra appena estensibile. Per favore correggimi se sbaglio.

+0

Un modo per estenderlo sarebbe utilizzare un parametro del modello enum, ma sono d'accordo che sarebbe un po 'più goffo. –

15

Un leggibile alternativa al [boost|std]::enable_if, tag e la specializzazione parziale per semplice invio fase di compilazione che mi piace è il seguente:

[di tener presente che booleani devono conversione in numeri interi, che gli array di lunghezza zero non sono validi e i modelli offendenti vengono scartati (SFINAE). Inoltre, char (*)[n] è un puntatore a una matrice di elementi n.]

template <typename T> 
void foo(T, char (*)[is_pod<T>::value] = 0) 
{ 
    // POD 
} 

template <typename T> 
void foo(T, char (*)[!is_pod<T>::value] = 0) 
{ 
    // Non POD 
} 

Essa ha anche il vantaggio di non dover classi esterne che inquinano il namespace. Ora, se si vuole esternare il predicato come nella tua domanda, si può fare:

template <bool what, typename T> 
void foo(T, char (*)[what] = 0) 
{ 
    // taken when what is true 
} 

template <bool what, typename T> 
void foo(T, char (*)[!what] = 0) 
{ 
    // taken when what is false 
} 

Usage:

foo<std::is_pod<T>::value>(some_variable); 
+3

+1 Molto intelligente. – templatetypedef

+0

Sì, è intelligente, ma se non conosci SFINAE, questo ha un fattore "WTF" serio che gli altri non hanno. Però apprezzo succintamente. –

+1

@Peter: SFINAE ha già un grande fattore WTF. L'uso di 'typename std :: enable_if :: type' offusca veramente la parte' qualunque'. Qui è chiaro, dentro le sue parentesi. –

9

In realtà entrambi sono solo del modello di dispacciamento dei tag. L'ex si chiama l'invio di tag per istanza e il secondo è il tag di dispacciamento per tipo.

Barend, l'autore principale di Boost.Geometry, explains entrambi i metodi e preferisce quest'ultimo. Questo è usato in Boost.Geometry ampiamente.Here're i vantaggi riassunti:

  • Non è necessario creare un'istanza del tag in quanto il suo unico scopo è quello di differenziare
  • E 'facile da definire nuovi tipi e le costanti in base ai tag
  • argomenti può essere invertito in l'interfaccia cioè dire distance(point, polygon); e distance(polygon, point); può entrambi hanno una sola implementazione
1

so che questa è una vecchia questione con la risposta già accettato, ma questo potrebbe essere una valida alternativa:

012.
template<typename T> 
std::enable_if_t<std::is_pod<T>::value> f(T pod) 
{ 
} 

template<typename T> 
std::enable_if_t<!std::is_pod<T>::value> f(T non_pod) 
{ 
}