SFINAE for detecting existence of non-member template function
tl;dr我想编写一个模板函数
我对斯芬娜有意见。假设我们需要支持函数
在C++中,我们不能创建仅在返回类型中不同的函数的多个重载,所以我们必须使函数
现在假设我们要编写一个模板函数
我试图写以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | #include <cstdio> #include <type_traits> // A workaround for void_t as described here: http://en.cppreference.com/w/cpp/types/void_t. template<typename... Ts> struct make_void { typedef void type;}; template<typename... Ts> using void_t = typename make_void<Ts...>::type; // An interface for a processor that receives a value of specific type. template<class T> class IProcessor { public: virtual void process(T value) = 0; }; // A processor for int. class IntProcessor : public IProcessor<int> { public: virtual void process(int value) override { printf("IntProcessor::process is called for value = %d ", value); } }; // Template prototype. template<class T> IProcessor<T>* CreateProcessor(); // Template specialization for int. template<> IProcessor<int>* CreateProcessor() { return new IntProcessor(); } // Detector of CreateProcessor. template<class, class=void> struct CreateProcessorImplemented : std::false_type { }; template<class T> struct CreateProcessorImplemented<T, void_t<decltype(CreateProcessor<T>())>> : std::true_type { }; // Specializations depending on existence of CreateProcessor. template <typename T> typename std::enable_if<CreateProcessorImplemented<T>::value, void>::type Process(T value) { IProcessor<T>* processor = CreateProcessor<T>(); processor->process(value); } template <typename T> typename std::enable_if<!CreateProcessorImplemented<T>::value, void>::type Process(T value) { printf("Processor for requested typename is unavailable "); } int main() { Process(42); Process("abc"); // static_assert(!CreateProcessorImplemented<char const*>::value,":("); /* This static_assert fails with an error: * code.cpp:56:5: error: static assertion failed: :( * static_assert(!CreateProcessorImplemented<char const*>::value,":("); */ } |
尽管这会导致链接错误:
1 2 | /tmp/ccTQRc9N.o:code.cpp:function std::enable_if<CreateProcessorImplemented<char const*, void>::value, void>::type Process<char const*>(char const*): error: undefined reference to 'IProcessor<char const*>* CreateProcessor<char const*>()' collect2: error: ld returned 1 exit status |
我的想法是,当我们解决
使其工作的一种方法是使用包装结构来这样运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | #include <cstdio> #include <type_traits> // A workaround for void_t as described here: http://en.cppreference.com/w/cpp/types/void_t. template<typename... Ts> struct make_void { typedef void type;}; template<typename... Ts> using void_t = typename make_void<Ts...>::type; // An interface for a processor that receives a value of specific type. template<class T> class IProcessor { public: virtual void process(T value) = 0; }; // A processor for int. class IntProcessor : public IProcessor<int> { public: virtual void process(int value) override { printf("IntProcessor::process is called for value = %d ", value); } }; // Template prototype. template<class T> struct ProcessorCreator: std::false_type { static IProcessor<T>* CreateProcessor(); }; // Template specialization for int. template<> struct ProcessorCreator<int>: std::true_type { static IProcessor<int>* CreateProcessor() { return new IntProcessor(); } }; // Detector of CreateProcessor. template<class, class=void> struct CreateProcessorImplemented : std::false_type { }; template<class T> struct CreateProcessorImplemented<T, typename std::enable_if<ProcessorCreator<T>::value>::type > : std::true_type { }; // Specializations depending on existence of CreateProcessor. template <typename T> typename std::enable_if<CreateProcessorImplemented<T>::value, void>::type Process(T value) { IProcessor<T>* processor = ProcessorCreator<T>::CreateProcessor(); processor->process(value); } template <typename T> typename std::enable_if<!CreateProcessorImplemented<T>::value, void>::type Process(T value) { printf("Processor for requested typename is unavailable "); } int main() { Process(42); Process("abc"); // static_assert(!CreateProcessorImplemented<char const*>::value,":("); /* This static_assert fails with an error: * code.cpp:56:5: error: static assertion failed: :( * static_assert(!CreateProcessorImplemented<char const*>::value,":("); */ } |
或者,可以删除模板声明,并使用函数重载传递IProcessor模板参数类型--方法是创建伪参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #include <cstdio> #include <type_traits> // A workaround for void_t as described here: http://en.cppreference.com/w/cpp/types/void_t. template<typename... Ts> struct make_void { typedef void type;}; template<typename... Ts> using void_t = typename make_void<Ts...>::type; // An interface for a processor that receives a value of specific type. template<class T> class IProcessor { public: virtual void process(T value) = 0; }; // A processor for int. class IntProcessor : public IProcessor<int> { public: virtual void process(int value) override { printf("IntProcessor::process is called for value = %d ", value); } }; IProcessor<int>* CreateProcessor(const int&) { return new IntProcessor(); } // Detector of CreateProcessor. template<class, class=void> struct CreateProcessorImplemented : std::false_type { }; template<class T> struct CreateProcessorImplemented<T, void_t<decltype(CreateProcessor(std::declval<T>()))>> : std::true_type { }; // Specializations depending on existence of CreateProcessor. template <typename T> typename std::enable_if<CreateProcessorImplemented<T>::value, void>::type Process(T value) { IProcessor<T>* processor = CreateProcessor(value); processor->process(value); } template <typename T> typename std::enable_if<!CreateProcessorImplemented<T>::value, void>::type Process(T value) { printf("Processor for requested typename is unavailable "); } int main() { Process(42); Process("abc"); // static_assert(!CreateProcessorImplemented<char const*>::value,":("); /* This static_assert fails with an error: * code.cpp:56:5: error: static assertion failed: :( * static_assert(!CreateProcessorImplemented<char const*>::value,":("); */ } |