[英]SFINAE for detecting existence of non-member template function
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 function CreateProcessor<T>()
. TL; DR我想编写一个模板函数
Process(T value)
,该函数针对不同的值表现不同,具体取决于是否存在非成员函数CreateProcessor<T>()
。 What can I do for that? 我该怎么办?
I have a problem with SFINAE. 我对SFINAE有问题。 Suppose we need to support function
CreateProcessor
that returns an implementation of interface IProcessor<T>
for some type type T
. 假设我们需要支持函数
CreateProcessor
,该函数返回某些类型T
的接口IProcessor<T>
实现。
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 by T
. 在C ++中,我们无法创建仅在返回类型上有所不同的函数的多个重载,因此我们必须使函数
CreateProcessor
也成为由T
参数化的模板函数。
Now suppose that we want to write a template function Process<T>(T value)
that works differently depending on existence of CreateProcessor<T>()
, namely it should process value
using the processor in case CreateProcessor<T>()
is implemented, otherwise it should result in error. 现在假设我们要编写一个模板函数
Process<T>(T value)
,该函数根据CreateProcessor<T>()
存在而不同地工作,即在实现CreateProcessor<T>()
情况下,它应该使用处理器来处理value
,否则将导致错误。
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 prototype IProcessor<T> CreateProcessor()
and compiler considers the decltype to be equal to IProcessor<T>
that is somehow logical but not what I need. 我的想法是,当我们解析
CreateProcessorImplemented<char const*>
, decltype(CreateProcessor<const char*>())
不会失败,因为存在模板原型IProcessor<T> CreateProcessor()
并且编译器认为decltype相等到IProcessor<T>
有点合乎逻辑,但不是我所需要的。
One way to make it work is to use wrapper struct to function CreateProcessor
like this: 使其工作的一种方法是使用包装器结构来像下面这样使用
CreateProcessor
:
#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: 或者,您可以删除模板声明并使用函数重载传递IProcessor模板参数类型-通过创建虚拟参数:
#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, ":(");
*/
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.