SFINAE 用于检测是否存在非成员模板函数
SFINAE for detecting existence of non-member template function
TL;DR 我想编写一个模板函数Process(T value)
,该函数根据是否存在非成员函数CreateProcessor<T>()
的不同值表现不同。我能为此做些什么?
我对SFINAE有问题。假设我们需要支持函数CreateProcessor
,该函数返回某种类型T
的接口IProcessor<T>
的实现。
C++我们不能创建仅在返回类型不同的函数的多个重载,因此我们必须使函数CreateProcessor
也成为模板函数,由T
参数化。
现在假设我们要编写一个模板函数Process<T>(T value)
,该函数根据CreateProcessor<T>()
的存在而以不同的方式工作,即它应该使用处理器处理value
以防实现CreateProcessor<T>()
,否则会导致错误。
我尝试编写以下代码:
#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 = %dn", 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 unavailablen");
}
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, ":(");
*/
}
虽然这会导致链接错误:
/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
我的想法是,当我们解析CreateProcessorImplemented<char const*>
时,decltype(CreateProcessor<const char*>())
不会失败,因为有一个模板原型IProcessor<T> CreateProcessor()
并且编译器认为 decltype 等于 IProcessor<T>
这在某种程度上是合乎逻辑的,但不是我需要的。
使其工作的一种方法是使用包装结构来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 = %dn", 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 unavailablen");
}
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 模板参数类型 -- 通过创建虚拟参数:
#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 = %dn", 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 unavailablen");
}
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, ":(");
*/
}
相关文章:
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 将公共但非静态的成员函数与ALGLIB集成
- 使用指向成员的指针将成员函数作为参数传递
- 将重载的成员函数传递给函数模板
- 我不小心调用了一个没有自己类对象的成员函数.但这是怎么回事呢
- 如何在C++中使用非静态成员函数作为回调函数
- C++错误C2600:无法定义编译器生成的特殊成员函数(必须首先在类中声明)
- 关联容器的下界复杂性:成员函数与非成员函数
- 在 C++ 中用派生类型重写成员函数
- 链表的泛型函数remove()与成员函数remove)
- 如何将lambda作为模板类的成员函数参数
- constexpr构造函数需要常量成员函数时出现问题
- 将自由函数绑定为类成员函数
- 区分非成员函数和头文件中的成员函数
- 如何从子成员函数修改父公共成员变量
- 保留对其他类的成员函数的引用
- 在运算符重载定义中使用成员函数(const错误)
- 内联如何影响模块接口中的成员函数
- 将成员函数指针作为参数传递给模板方法