SFINAE用于检测非成员模板函数的存在 [英] SFINAE for detecting existence of non-member template function
问题描述
TL; DR 我要写一个模板函数 Process(T value)
,它对不同的值有不同的行为,非成员函数 CreateProcessor< T>()
。我能做些什么?
我有一个SFINAE的问题。假设我们需要支持对于某些类型类型<$ c>返回接口 IProcessor
的实现的函数 CreateProcessor
$ c> T 。
在C ++中,我们不能创建仅仅在返回类型不同的函数的多个重载, make函数 CreateProcessor
也是由 T
参数化的模板函数。
现在假设我们要写一个模板函数 我试图写下面的代码: 虽然这会导致链接错误: Process< T>(T value)
,它根据 CreateProcessor< T> ()
,即在 CreateProcessor< T>()$ c $时使用处理器处理
value
c>
#include< cstdio>
#include< type_traits>
// void_t的解决方法如下所示:http://en.cppreference.com/w/cpp/types/void_t。
template< typename ... Ts> struct make_void {typedef void type;};
template< typename ... Ts>使用void_t = typename make_void< Ts ...> :: type;
//接收特定类型的值的处理器的接口。
template< class T>
class IProcessor {
public:
virtual void process(T value)= 0;
};
// int的处理器。
class IntProcessor:public IProcessor< int> {
public:
virtual void process(int value)override {
printf(IntProcessor :: process is called for value =%d\\\
,value);
}
};
//模板原型。
template< class T>
IProcessor< T> * CreateProcessor();
// int模板专用化。
模板<>
IProcessor< int> * CreateProcessor(){
return new IntProcessor();
}
// CreateProcessor的检测器。
template< class,class = void>
struct CreateProcessorImplemented:std :: false_type {};
template< class T>
struct CreateProcessorImplemented< T,void_t< decltype(CreateProcessor< T>())> :std :: true_type {};
//根据CreateProcessor的存在而进行的特殊化。
template< typename T>
typename std :: enable_if< CreateProcessorImplemented< T> :: value,void> :: type Process(T value){
IProcessor< T> * processor = 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,:();
/ *此static_assert失败并出现错误:
* code。 cpp:56:5:错误:静态断言失败::(
* static_assert(!CreateProcessorImplemented< char const *> :: value,:();
* /
}
/tmp/ccTQRc9N.o:code.cpp:function std :: enable_if< CreateProcessorImplemented< char const *,void> :: value,void> :: type Process< char const * ;}(char const *):error:未定义引用'IProcessor< char const *> * CreateProcessor< char const *>()'
collect2:错误:ld返回1退出状态
<我的想法是,当我们解决 CreateProcessorImplemented< char const *>
$ b < ,decltype(CreateProcessor< const char *>())
不会失败,因为有一个模板原型IProcessor< T> CreateProcessor / code>并且编译器认为decltype等于
IProcessor
,这是某种逻辑的,但不是我需要的。 解决方案使其工作的一种方法是使用wrapper struct来
CreateProcessor
>
#include< cstdio>
#include< type_traits>
// void_t的解决方法如下所示:http://en.cppreference.com/w/cpp/types/void_t。
template< typename ... ts> struct make_void {typedef void type;};
template< typename ... Ts>使用void_t = typename make_void< Ts ...> :: type;
//接收特定类型的值的处理器的接口。
template< class T>
class IProcessor {
public:
virtual void process(T value)= 0;
};
// int的处理器。
class IntProcessor:public IProcessor< int> {
public:
virtual void process(int value)override {
printf(IntProcessor :: process is called for value =%d\\\
,value);
}
};
//模板原型。
template< class T>
struct ProcessorCreator:std :: false_type {
static IProcessor< T> * CreateProcessor();
};
// int模板专用化。
模板<>
struct ProcessorCreator< int> ;: std :: true_type {
static IProcessor< int> * CreateProcessor(){
return new IntProcessor
}
};
// 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 {};
//根据CreateProcessor的存在而进行的特殊化。
template< typename T>
typename std :: enable_if< CreateProcessorImplemented< T> :: value,void> :: type Process(T value){
IProcessor* 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,:();
/ *这个static_assert失败并出现错误:
* code。 cpp:56:5:错误:静态断言失败::(
* static_assert(!CreateProcessorImplemented< char const *> :: value,:();
* /
}
或者,您可以删除模板声明,并使用函数重载传递IProcessor模板参数类型 - 参数:
#include< cstdio>
#include< type_traits>
// void_t的解决方法如下所示:http://en.cppreference.com/w/cpp/types/void_t。
template< typename ... Ts> struct make_void {typedef void type;};
template< typename ... Ts>使用void_t = typename make_void< Ts ...> :: type;
//接收特定类型的值的处理器的接口。
template< class T>
class IProcessor {
public:
virtual void process(T value)= 0;
};
// 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();
}
// 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 {};
//根据CreateProcessor的存在而进行的特殊化。
template< typename T>
typename std :: enable_if< CreateProcessorImplemented< T> :: value,void> :: type Process(T value){
IProcessor< T> * processor = 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,:();
/ *此static_assert失败并出现错误:
* code。 cpp:56:5:错误:静态断言失败::(
* static_assert(!CreateProcessorImplemented< char const *> :: value,:();
* /
}
TL;DR I want to write a template function
Process(T value)
that behaves differently for different values depending on the existence of a non-member functionCreateProcessor<T>()
. What can I do for that?I have a problem with SFINAE. Suppose we need to support function
CreateProcessor
that returns an implementation of interfaceIProcessor<T>
for some type typeT
.In C++ we can't create several overloads of a function that differ only in return type, so we have to make function
CreateProcessor
also be template function parametrized byT
.Now suppose that we want to write a template function
Process<T>(T value)
that works differently depending on existence ofCreateProcessor<T>()
, namely it should processvalue
using the processor in caseCreateProcessor<T>()
is implemented, otherwise it should result in error.I attempted to write the following code:
#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\n", 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\n"); } 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, ":("); */ }
Though this results in linkage error:
/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
My idea is that when we resolve
CreateProcessorImplemented<char const*>
,decltype(CreateProcessor<const char*>())
doesn't fail because there is a template prototypeIProcessor<T> CreateProcessor()
and compiler considers the decltype to be equal toIProcessor<T>
that is somehow logical but not what I need.解决方案One way to make it work is to use wrapper struct to function
CreateProcessor
like this:#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\n", 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\n"); } 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, ":("); */ }
Alternatively you could remove template declaration and pass the IProcessor template parameter type using function overloadings -- by creating dummy argument:
#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\n", 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\n"); } 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, ":("); */ }
这篇关于SFINAE用于检测非成员模板函数的存在的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!