2016-06-17 37 views
6

To pytanie zostało zainspirowane moim poprzednim pytaniem No template parameter deduction of parameter pack.Włącz szablon tylko dla określonej klasy szablonów.

Rozważmy następujący przykład kodu:

#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 
} 

Zobacz także http://coliru.stacked-crooked.com/a/5f2a2e816eef6afd

Funkcja szablonu makeTypeErasedCallbackHandle teraz bierze żadnej klasy jako parametr wejściowy. Czy jest jakiś sposób zapewnienia (na przykład z statycznym assert lub enable_if), że tylko Callback<FArgs...>::Handle (z dowolnym FArgs) jest dozwolone jako H? Przykład z kompilacją Callback<int>::Handle będzie kompilowany, natomiast std::string zawiedzie.

+0

Możliwe duplikat [Doing static \ _assert, że typ szablonu jest innym szablonem] (http: // stackoverflow.com/questions/17390605/doing-a-static-assert-that-a-template-type-is-another-template) –

Odpowiedz

1

określić typ ciągu swojej klasie Handle, i odnoszą się do tego typu wewnątrz 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

to zawiedzie podczas instancji dla każdego H że nie określa typu zagnieżdżonego.


Z trochę więcej wysiłku, można static_assert produkować znaczącą wiadomość do klienta, przy jednoczesnym zwiększeniu elastyczności poprzez rozwiązania typu cech. Ma to tę zaletę, że callback_impl::is_callback mogą być specjalnie przystosowane do dowolnych typów uchwytu:

#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

wyjściowy:

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

Można dokonać fikcyjny element dla swojej rodziny klasy i wywołać go na stronie substytucyjnego: http://coliru.stacked-crooked.com/a/d5738766fd7ac45f

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

...

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

Więcej sposobem jest wykorzystanie cech klasowych lub statyczne twierdzi, z bibliotek takich jak BOOST (niektóre z nich są już częścią języka).

EDYTOWANIE: http://coliru.stacked-crooked.com/a/2a3adcb9d9dd274c W ten sposób jest nieco lepiej, var zamiast wywołania.

1

Jednym ze sposobów, aby to zrobić jest przekazując kilka dodatkowych argumentów:

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 
} 

To sprawia, że ​​korzystanie z identity trick (Stephan T. Lavavej) dla prowadzenia odliczenia typu.

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 {}; 
} 

ten powinien pracy i tylko pozwalają używać, funkcja na oddzwanianie