【发布时间】:2016-06-26 16:14:28
【问题描述】:
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 = %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, ":(");
*/
}
虽然这会导致链接错误:
/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> 这在某种程度上是合乎逻辑的,但不是我的需要。
【问题讨论】:
-
CreateProcessorImplemented始终派生自std::true_type,因为函数不需要具有decltype的实现来告诉您返回类型(请参阅std::declval)。 -
@Simple,这很有道理。我相信没有办法在编译时检查函数是否有实现,因为它需要链接器的知识,对吧?
-
@WojciechFrohmberg 的答案是另一种选择。您将
struct与静态成员函数而不是非成员函数一起使用。您可以检测是否由于struct没有被专门化而没有静态成员函数。它基本上是一种类型特征。
标签: c++ templates template-meta-programming sfinae