2016-06-17 28 views
6

Questa domanda è ispirata alla mia domanda precedente No template parameter deduction of parameter pack.Abilita modello solo per classi di modelli specifici

Considerate seguente esempio di codice:

#include <memory> 
#include <string> 

template<typename... FArgs> 
class Callback 
{ 
    public: 
    class Handle{}; 
}; 

class BaseCallbackHandle 
{ 
}; 

using TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>; 

template<typename H> 
TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H handle) 
{ 
    return {}; 
} 

int main() 
{ 
    Callback<int>::Handle h; 
    std::string s; 
    makeTypeErasedCallbackHandle(h); //should compile fine 
    makeTypeErasedCallbackHandle(s); //should raise a compile error 
} 

Vedi anche http://coliru.stacked-crooked.com/a/5f2a2e816eef6afd

Il modello makeTypeErasedCallbackHandle funzione di ora prende qualsiasi classe come parametro di input. C'è un modo per garantire (ad esempio con static-assert o enable_if), che solo (con qualsiasi FArgs) è consentito come H? L'esempio con Callback<int>::Handle deve essere compilato, mentre std::string avrà esito negativo.

+0

Possibile duplicato di [Doing a static \ _assert che un tipo di modello è un altro modello] (http: // stackoverflow.com/domande/17390605/fare-a-static-assert-che-un-modello-tipo-è-un altro-modello) –

risposta

1

definire un tipo all'interno della vostra classe Handle, e si riferiscono a quel tipo all'interno makeTypeErasedCallbackHandle():

#include <memory> 
#include <string> 

template <typename... FArgs> 
struct Callback { 
    struct Handle { 
     using callback_type = Callback<FArgs...>;  
    }; 
}; 

struct BaseCallbackHandle { 
}; 

using TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>; 

template <typename H> 
TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H handle) { 
    using callback_type = typename H::callback_type; 

    return {}; 
} 

int main() { 
    Callback<int>::Handle h; 
    std::string s; 
    makeTypeErasedCallbackHandle(h); //should compile fine 
    makeTypeErasedCallbackHandle(s); //should raise a compile error 
} 

Live example

Questo fallirà durante la creazione di un'istanza per qualsiasi H che non definisce il tipo nidificato.


Con un piccolo sforzo, si può static_assert per produrre un messaggio significativo per il cliente, mentre allo stesso tempo aumentando la flessibilità della soluzione tramite caratteri morfologici. Questo ha il vantaggio che callback_impl::is_callback possono essere specializzati per tipi di maniglia arbitrari:

#include <memory> 
#include <string> 

namespace callback_impl { 

struct callback_identification_type {}; 

template <typename T, typename = void> 
struct is_callback : std::false_type {}; 

template <typename T> 
struct is_callback<T, 
    std::enable_if_t<std::is_same<typename T::callback_id_type, 
        callback_identification_type>::value>> 
: std::true_type {}; 

} 

template <typename... FArgs> 
struct Callback { 
    struct Handle { 
     using callback_id_type = callback_impl::callback_identification_type;  
    }; 
}; 

struct BaseCallbackHandle { 
}; 

using TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>; 

template <typename H> 
TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H handle) { 
    static_assert(callback_impl::is_callback<H>::value, 
        "The handle type is not a member of a recognised Callback<T...>"); 
    return {}; 
} 

int main() { 
    Callback<int>::Handle h; 
    std::string s; 
    makeTypeErasedCallbackHandle(h); //should compile fine 
    makeTypeErasedCallbackHandle(s); //should raise a compile error 

    return 0; 
} 

Live example

uscita:

g++ -std=c++14 -O2 -Wall -Wno-unused-local-typedefs -pedantic -pthread main.cpp && ./a.out 
main.cpp: In instantiation of 'TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H) [with H = std::__cxx11::basic_string<char>; TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>]': 
main.cpp:41:35: required from here 
main.cpp:32:5: error: static assertion failed: The handle type is not a member of a recognised Callback<T...> 
    static_assert(callback_impl::is_callback<H>::value, 
    ^~~~~~~~~~~~~ 
0

È possibile effettuare membro fittizia per la vostra famiglia di classe, e lo chiamano sul lato sostituzione: http://coliru.stacked-crooked.com/a/d5738766fd7ac45f

class Handle 
{ 
    public: 
    static void doNothing(){} 
}; 

...

TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H handle) 
{ 
    H::doNothing(); 
    return {}; 

Altro metodo standard è quello di utilizzare i tratti di classe o statico asserisce, da librerie come BOOST (potrebbe essere che alcune di esse siano già parte del linguaggio).

MODIFICA: http://coliru.stacked-crooked.com/a/2a3adcb9d9dd274c Questo modo è leggermente migliore, var invece di chiamata.

1

Un modo per farlo è quello di passare alcuni argomenti extra:

template <typename... Pack> struct packer {}; 

using TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>; 

template <typename... T> 
TypeErasedCallbackHandle makeTypeErasedCallbackHandle(typename Callback<T...>::Handle h, T...) 
{ 
    return {}; 
} 

template <typename... T> 
TypeErasedCallbackHandle makeTypeErasedCallbackHandle_2(typename Callback<T...>::Handle h, packer<T...>) 
{ 
    return {}; 
} 

int main() 
{ 
    Callback<int>::Handle h; 
    std::string s; 
    makeTypeErasedCallbackHandle(h, 0); //should compile fine 
    // OR 
    makeTypeErasedCallbackHandle_2(h, packer<int>()); 

    //makeTypeErasedCallbackHandle(s); //should raise a compile error 
} 

Questo fa uso di identity trick (da Stephan T. Lavavej) per fare il tipo di detrazione.

0
class BaseCallback 
{ 
    ~BaseCallback() = 0; 
}; 

template<typename... FArgs> 
class Callback : public BaseCallback 
{ 
    ~Callback(){...} 
    ... 
}; 

template<typename H> 
TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H handle) 
{ 
    static_assert(std::is_base_of<BaseCallback, H>::value, "Must use a callback"); 
    return {}; 
} 

Questo dovrebbe lavoro e solo consentono di utilizzare quel funzione sui callback