2012-08-08 23 views
5

C'è un modo per confrontare il risultato di decltype in C++ 11?confronto decltype

In altre parole, perché è questo codice non valido:

template<typename T, typename U> 
void func(T& t, U& u) { 
    if(decltype(t) == decltype(u)) { 
     // Some optimised version for this case 
    } else { 
     // A more general case for differing types 
    } 
} 

So che in alcuni casi questo particolare problema può essere risolto dal modello di specializzazione parziale; la mia domanda riguarda il confronto di decltype s.

Modifica: La domanda è arrivata nel corso del tentativo di fornire le impostazioni predefinite per le funzioni gratuite tramite SFINAE. Forse una domanda migliore sarebbe stato il motivo per cui questo non è valido:

template<bool B> 
bool SomeFunction() { ... } 

template<typename T, typename U> 
bool SomeFunctionWrapper(T& t, U& u) { 
    SomeFunction<decltype(t) == decltype(u)>(); 
} 

allora ho trovato un'altra soluzione (che non coinvolge i modelli a tutti), ma ad un certo punto ho provato questo:

// If it exists, the free function is defined as 
// bool AFreeFunction(); 
typedef struct { char } undefined; 
template<typename T = void> 
undefined AFreeFunction(); 

template<bool B> 
bool AFreeFunctionWrapper_() { 
    return false; 
} 

template<> 
bool AFreeFunctionWrapper_<false>() { 
    return AFreeFunction(); 
} 

bool AFreeFunctionWrapper() { 
    return AFreeFunctionWrapper_<decltype(AFreeFunction()) == decltype(undefined)>(); 
} 

Alla fine ho ottenuto una variante di questa strategia che funzionava con GCC 4.6, ma poi ho scoperto che gli argomenti del modello predefinito non sono consentiti per le funzioni dei template in MSVC, anche nel 2012 RC. Quindi la soluzione finale è la seguente:

class AFreeFunction { 
public: 
    operator bool() { return false; } 
}; 

Se la funzione è definita, viene richiamata. Se non lo è, viene invece interpretato come un costruttore per la classe, che viene quindi implicitamente trasmesso a bool.

+0

Si noti che il modulo di funzione gratuito di cui sopra non può essere risolto con una specializzazione parziale; ha bisogno di essere riformulato come modello di classe per consentirlo. – Tom

+0

No, non è necessario essere un modello di classe, può essere un semplice insieme di modelli di funzioni * overloaded *. –

+0

@ n.m. Buon punto – Tom

risposta

8

In genere si risolve questo problema tramite l'invio di tag. Inoltre, hai già il "tipo dichiarato" di t e u - T& e U&, rispettivamente. Per confrontare i tipi per l'uguaglianza, è possibile utilizzare std::is_same. Tuttavia, la risoluzione di sovraccarico risolve già questo problema per voi:

template<class T> 
void f(T& v1, T& v2){ ... } // #1 

template<class T, class U> 
void f(T& t, U& u){ ... } // #2 

# 1 è più specializzata di 2 # se entrambi gli argomenti a f sono dello stesso tipo.Se, per qualsiasi motivo, ostini a risolvere questo tipo passante confronto manuale, ecco come dovrebbe apparire l'applicazione dei punti prima citati:

#include <type_traits> 

namespace detail{ 
template<class T, class U> 
void f(T& t, U& u, std::true_type){ ... } // #1 

template<class T, class U> 
void f(T& t, U& u, std::false_type){ ... } // #2 
} // detail:: 

template<class T, class U> 
void f(T& t, U& u){ 
    detail::f(t, u, std::is_same<T,U>()); // tag dispatching 
} 

std::is_same deriveranno da std::true_type se entrambi i tipi sono gli stessi, e da std::false_type se non.

+0

il compilatore elimina sempre il tipo vero/falso creato? Avrei specializzato un modello su un 'bool' e passato invece nel tipo. In questo modo, non è possibile che venga creato un tipo inutile. – KitsuneYMG

+0

L'ho capito e ho pensato che probabilmente era la risposta, ma cercavo di raccogliere altre idee su di esso. Il contesto reale era che stavo cercando di creare un 'modello bool Function() {...}' e 'template <> bool Function () {...}', quindi istanziarlo come 'bool b = Function (); '. Potrei aggiungere questo dettaglio alla domanda. – Tom

4

Perché non è valido? Il risultato di un decltype è, beh, un tipo. Quindi sta dicendo qualcosa come

if (int == int) 

che la lingua ovviamente non consente.

È necessario separare le due parti della funzione e inserire la parte specializzata in una funzione in una classe specializzata e inoltrare la chiamata. È doloroso.

Oppure, è possibile utilizzare typeid o informazioni sul tipo di runtime, se l'implementazione lo implementa correttamente, anche se questo verrà rimandato a quando il programma viene eseguito (che consente un minor numero di ottimizzazioni).

+0

Forse ho semplificato eccessivamente la domanda. Capisco che 'if (int == int)' non è valido. Non mi è chiaro perché 'some_template ' non è valido. Considera l'analogia con 'some_template ' - perfettamente valido, e 'decltype' dovrebbe essere una sorta di estensione naturale di' sizeof'. – Tom

+0

@Tom: 'sizeof' restituisce un * valore *. 'decltype'" restituisce "un * tipo *. Sono animali diversi .. – Mehrdad

+1

non del tutto vero. 'sizeof' restituisce un valore _constant_ che consente al compilatore di risolvere l'istanza del template in fase di compilazione. La mia domanda è stata la ragione per cui il comitato degli standard non ha consentito il confronto in fase di compilazione dei tipi poiché consentono confronti in fase di compilazione di dimensioni di tipi. – Tom

3

È possibile farlo utilizzando SFINAE (std::enable_if):

template<typename T, typename U> 
typename std::enable_if<std::is_same<T, U>::value, void>::type func(T& t, U& u) { 
    std::cout << "same\n"; 
} 
template<typename T, typename U> 
typename std::enable_if<!std::is_same<T, U>::value, void>::type func(T& t, U& u) { 
    std::cout << "different\n"; 
} 

come dice Mehrdad, decltype(t) e decltype(u) sono i tipi (T & e U & rispettivamente), non i valori, in modo da non possono essere confrontati a livello di valore di espressione, ma deve essere confrontato a livello di meta-espressione (modello).

+0

Capisco che è così che funziona; mi chiedo solo se c'è una buona ragione per cui il comitato degli standard non ha permesso di confrontare il risultato di "decltype". Vedi la domanda modificata per quello che stavo cercando di raggiungere; in realtà il confronto stava accadendo in un contesto di argomento modello, ed è abbastanza vicino a ciò che suggerisci. Non ero realmente a conoscenza di 'std :: enable_if' e non avevo sentito parlare di' std :: is_same', comunque. – Tom

+0

@Tom 'decltype' non è una funzione, è un operatore, quindi il suo" risultato "non deve comportarsi come un valore. – ecatmur

+0

Non sono sicuro di seguire questo ragionamento. Il risultato di ogni altro operatore si comporta come un valore, dopo tutto. 'sizeof' è un operatore molto strettamente correlato che restituisce qualcosa che si comporta come un valore ma può essere paragonato ad altri valori (costanti) al momento della compilazione nello stesso modo in cui io (erroneamente) mi aspetto che decltype debba essere eseguito. – Tom