SFINAE用于检测非成员模板函数的存在 [英] SFINAE for detecting existence of non-member template function

查看:138
本文介绍了SFINAE用于检测非成员模板函数的存在的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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>() 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 function CreateProcessor<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 interface IProcessor<T> for some type type 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.

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.

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.

解决方案

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆