2012-06-09 8 views
6

Edit: La risposta breve alla mia domanda è che ho avuto una visione sbagliata di ciò che SFINAE può fare e non si controlla la funzione di corpo a tutti: does sfinae instantiates a function body?Esiste un modo per rilevare se esiste una funzione e può essere utilizzata in fase di compilazione?

ho un problema simile a questo: Is it possible to write a template to check for a function's existence?

La differenza è che non voglio solo controllare se la funzione esiste, ma voglio anche sapere se effettivamente passerà SFINAE. Ecco un esempio di quello che sto cercando di realizzare:

struct A 
{ 
    void FuncA() { std::cout << "A::FuncA" << std::endl; } 
}; 

struct B 
{ 
    void FuncA() { std::cout << "B::FuncA" << std::endl; } 
    void FuncB() { std::cout << "B::FuncB" << std::endl; } 
}; 

template<typename T> 
struct Inter 
{ 
    void FuncA() { t.FuncA(); } 
    void FuncB() { t.FuncB(); } 

    T t; 
}; 

// Always takes some sort of Inter<T>. 
template<typename InterType> 
struct Final 
{ 
    void CallFuncs() 
    { 
     // if(t.FuncA() exists and can be called) 
      t.FuncA(); 

     // if(t.FuncB() exists and can be called) 
      t.FuncB(); 
    } 

    InterType t; 
}; 

void DoEverything() 
{ 
    Final<Inter<A>> finalA; 
    Final<Inter<B>> finalB; 

    finalA.CallFuncs(); 
    finalB.CallFuncs(); 
} 

Si noti che in CallFuncs(), sia FuncA() e FuncB() esisterà sempre, ma non può compilare a seconda del tipo T usata in Inter. Quando ho provato ad usare la risposta nella domanda sopra collegata mi è sembrato sempre darmi la verità che sto supponendo è perché controlla solo che la funzione esista, non che possa essere effettivamente compilata (anche se non posso escluderla non ho vite qualcosa ...)

al fine di richiamare le funzioni condizionale Immagino che posso usare enable_if come tale:

template<typename InterType> 
typename std::enable_if< ! /* how to determine if FuncA can be called? */>::type TryCallFuncA(InterType& i) 
{ 
} 
template<typename InterType> 
typename std::enable_if</* how to determine if FuncA can be called? */>::type TryCallFuncA(InterType& i) 
{ 
    i.FuncA(); 
} 

template<typename InterType> 
typename std::enable_if< ! /* how to determine if FuncB can be called? */>::type TryCallFuncB(InterType& i) 
{ 
} 
template<typename InterType> 
typename std::enable_if</* how to determine if FuncB can be called? */>::type TryCallFuncB(InterType& i) 
{ 
    i.FuncB(); 
} 

template<typename InterType> 
struct Final 
{ 
    void CallFuncs() 
    { 
     TryCallFuncA(t); 
     TryCallFuncB(t); 
    } 

    InterType t; 
}; 

ma non sono sicuro se c'è ogni modo possibile ottieni un valore booleano da passare in enable_if. C'è un modo per ottenerlo o devo ricorrere a qualche tipo di tratto di tipo mantenuto manualmente che indica se le funzioni esistono?

Per quel che vale per quanto riguarda la disposizione C++ set di 11 funzione, sto usando MSVC 2010.

modificare: Per aggiungere una nota importante, nella mia situazione attuale l'implementazione della classe Inter è effettivamente opaco al punto in cui ho bisogno di determinare se Inter :: FuncA/FuncB verrà compilato, in modo che non possa semplicemente chiarire i tipi di figlio e controllare l'esistenza della funzione su di essi.

risposta

8

non ho il tempo di controllare questo ora, ma è possibile aggiungere una specializzazione di Final:. template <typename T> struct Final< Inner<T> >; (che aiuta anche a garantire che il tipo è sempre un Inner Con che è possibile estrarre il tipo utilizzato per istanziare Inter .

Ora il secondo problema è come utilizzare SFINAE per rilevare se una funzione di membro esiste credo che questo non dovrebbe essere troppo complesso (se non è necessario per rendere questo generico):.

// Find out whether U has `void f()` member 
template <typename U> 
struct has_member_f { 
    typedef char yes; 
    struct no { char _[2]; }; 
    template<typename T, void (T::*)() = &T::f> 
    static yes impl(T*); 
    static no impl(...); 

    enum { value = sizeof(impl(static_cast<U*>(0))) == sizeof(yes) }; 
}; 

Potresti essere in grado di estenderlo un po 'per renderlo un po' più ge neric, ma il nome della funzione non credo che tu possa essere generico. Naturalmente, è possibile scriverlo come una macro che genera has_member_##arg e utilizza &T:: arg. Il tipo di membro è probabilmente più facile da generalizzare ...

In alternativa, poiché non penso che questo possa essere reso generico, è possibile utilizzare il trucco all'interno di has_member direttamente nel tuo tipo: fornire due sovraccarichi callFuncA, un modello con il secondo argomento opzionale con la firma che si desidera e il valore predefinito è &T::FuncA che inoltra la chiamata, l'altro con ellissi che è un noop. Quindi callFuncs chiamerebbe callFuncA e callFuncB e SFINAE invierà allo spedizioniere o al mezzogiorno e otterrai il comportamento desiderato.

template<typename T> 
struct Final< Inter<T> > 
{ 
    template <typename U, void (U::*)() = &U::FuncA> 
    void callFuncA(Inter<T>* x) { 
     x.FuncA(); 
    } 
    void callFuncA(...) {} 

    void CallFuncs() { 
     callFuncA(&t);     // Cannot pass nonPOD types through ... 
     // Similarly TryCallFuncB(t); 
    } 
    Inter<T> t; 
}; 
+0

Se ricordo correttamente, c'è un problema con gli argomenti di default e modelli di funzioni in C++ 03 che renderebbero questa soluzione disponibile solo in C++ 11. Potresti confermare/infermare? –

+0

@MatthieuM .: corretto, questa è una soluzione solo per C++ 11. In C++ non è possibile fornire l'argomento predefinito al modello di funzione. Questa è una caratteristica davvero carina che è volata sotto il radar nel nuovo standard e richiesta in molti dei casi in cui lo standard impone che una particolare funzione * non partecipi alla risoluzione di sovraccarico a meno che X non sia vero. * (Cioè SFINAE con mandato). –

+0

Sfortunatamente non è supportato in VS (2010 o 2012). Ai fini della comprensione della soluzione, l'argomento predefinito & U :: FuncA non solo viene verificato che esista ma anche che, se viene chiamato, verrà compilato correttamente (ad esempio il tipo fornito all'Inter potrebbe essere una classe template (per esempio struct A ) e l'implementazione di FuncA/FuncB dipende da quel tipo)? – Screndib

Problemi correlati