2015-01-28 10 views
7

Esiste un modo per forzare i compilatori C++ a eseguire la ricerca del nome per un determinato simbolo durante l'istanziazione del modello (e non prima)?C++ applica la ricerca nome secondo passaggio nella funzione modello

Dato il seguente codice:

template <class T> 
auto wrapper(T t) -> decltype(f(t)) 
{ 
    return f(t); 
} 

unsigned char f(int x) { return x % 256; } 
unsigned char f(unsigned char x) { return x; } 

int main(int, char **) 
{ 
    auto x = wrapper(3100); 
    return 0; 
} 

C'è qualcosa che posso fare (a parte spostare la definizione di f verso l'alto), al fine di rendere il codice di compilazione e dare gli stessi risultati come se tutte le definizioni di f erano disponibili prima della definizione di wrapper?

Non sono riuscito a trovare nulla, probabilmente perché non so come formulare correttamente questa domanda. Tutti i tipi di argomento di f possono essere considerati tipi definiti dall'utente, se questo aiuta.

+0

Perché non si utilizza la specializzazione del modello? – Chiel

+0

@ Chiel: Grazie, ottima idea! Quindi sostituirò 'f' con un modello di classe? Funziona, ma avevo sperato in una soluzione che non mi richiedesse di cambiare la definizione di 'f' (molto). O hai qualcos'altro in mente? –

+0

È possibile utilizzare un valore predefinito che si applica alla maggior parte dei casi e alla specializzazione laddove richiesto, vedere la mia risposta. – Chiel

risposta

2

C'è qualche modo per forzare compilatori C++ per eseguire la ricerca dei nomi per un data simbolo durante istanziazione di template (e non prima)?

Sì. Prima di tutto, il nome deve essere dipendente. Il nome f in wrapper quando utilizzato come f(t) dipende dal fatto che t dipende dal tipo. [Temp.dep]/1:

In un'espressione della forma:

                postfix-espressione (espressione-list opt)

dove il postfix-espressione è un qualificato-id, il qualificato-id denota un nome dipendente se

  • qualsiasi espressioni nella espressione-list è un pacchetto di espansione (14.5.3),
  • qualsiasi espressioni nella espressione-list è un tipo dipendente dalla espressione (14.6.2.2), o
  • se il qualificato-id è un modello -id in cui nessuno degli argomenti modello dipende da un parametro di modello.

Il problema è che i nomi dichiarati dopo il modello stesso, vale a dire solo in l'istanza, ma non il contesto definizione, possono solo essere trovati utilizzando argomento nome dipendente ricerca. I suoi f sovraccarichi solo prendere tipi fondamentali, ma quelli non hanno lo spazio dei nomi globale associato con loro in base al [basic.lookup.argdep]/2:

Se T è un tipo fondamentale, i suoi gruppi associati di spazi dei nomi e Le classi sono entrambe vuote.

Così i f s stati dichiarati non possono mai essere trovato se gli argomenti sono dello stesso tipo dei parametri. Un piccolo trucco può aiutare:

template <typename T> 
struct refwrap 
{ 
    T&& t; 
    refwrap(T&& t) : t(std::forward<T>(t)) {} 
    operator T&&() {return std::forward<T>(t);} 
}; 

template <typename T> 
auto make_refwrap(T&& t) -> refwrap<T> // making use of reference collapsing 
{ return {std::forward<T>(t)}; }   // inside refwrap to get forwarding 

Questo modello, quando ha dichiarato nel namespace globale, causerà ADL a considerarlo. Riscrivere wrapper come segue:

template <class T> 
auto wrapper(T t) -> decltype(f(make_refwrap(t))) 
{ 
    return f(make_refwrap(t)); 
} 

Demo. Questo non è il modo corretto di farlo, poiché fallirà in scenari più complessi.

+0

Ah, ora vedo qual è il problema originale che ha portato a fare questa domanda. La mia funzione 'f' era definita in un diverso spazio dei nomi (cioè uguale a' wrapper') quindi l'argomento. –

+0

Quale clausola dice che può essere trovata solo con ADL? Inoltre, quale sarebbe il modo "corretto" per farlo? – 0x499602D2

1

Ciò funzionerebbe con la specializzazione del modello. Nota che devi decidere quale sia la funzione predefinita, perché non riesco a vederla in questione.

// default function 
template <class T> 
unsigned char f(T x) { return x; } 

// specialization for int 
template <> 
unsigned char f(int x) { return x % 256; } 

int main(int, char **) 
{ 
    auto x = f(3100); 
    return 0; 
} 
+0

Il mio esempio era troppo semplice e poteva essere facilmente interpretato erroneamente. Si prega di non assumere che ci sia un * caso * predefinito *. Per favore non rimuovere la funzione 'wrapper', perché la domanda riguarda davvero quella funzione, che, ovviamente, è più complessa della funzione di identità nella mia situazione. –

+0

Perché il sovraccarico semplice non è sufficiente? – Chiel

+1

Si suppone che la funzione 'wrapper' usi un'altra funzione' f' per ottenere un risultato intermedio e per trasformare quel risultato. Per definire 'wrapper' tutte le definizioni di' f' devono essere disponibili al momento in cui 'wrapper' è definito, poiché i nomi vengono ricercati durante l'analisi della definizione del modello. Questo è un comportamento indesiderato nel mio caso, perché si suppone che gli utenti della libreria siano in grado di fornire definizioni aggiuntive di 'f' e avrei difficoltà a dire loro che devono includere le loro definizioni prima che includano determinati altri file. –

0

Il seguente codice non è abbastanza pulito, ma illustra come modello di classe di specializzazione può essere utilizzato al fine di risolvere il problema. Mantiene l'interfaccia originale (ad esempio f e wrapper può essere utilizzata nello stesso modo di prima).

Grazie per avermi dato i consigli giusti. Sono aperto a una soluzione meno verbosa.

#include <type_traits> 

template <class ...> 
struct F; 

template <class T> 
auto wrapper(T t) 
    -> decltype(F<typename std::decay<T>::type>::f(t)) 
{ 
    return F<typename std::decay<T>::type>::f(t); 
} 

template <> 
struct F<unsigned char> 
{ 
    static unsigned char f(unsigned char x) { return x; } 
}; 

template <> 
struct F<int> 
{ 
    static unsigned char f(int x) { return x % 256; } 
}; 

template <class T> 
auto f(T t) 
    -> decltype(F<typename std::decay<T>::type>::f(t)) 
{ 
    return F<typename std::decay<T>::type>::f(t); 
} 

int main(int, char **) 
{ 
    auto x = wrapper(3100); 
    return 0; 
} 
Problemi correlati