SFINAE:编译器不选择专门的模板类 [英] SFINAE: Compiler doesn't pick the specialized template class

查看:147
本文介绍了SFINAE:编译器不选择专门的模板类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有 SFINAE 问题:



在下面的代码,我想C ++编译器选择专门的函子并打印特殊,但它是打印一般。

  #include< iostream> 
#include< vector>

template< class T,class V = void>
struct Functor {
void operator()()const {
std :: cerr<< 一般< std :: endl;
}
};

template< class T>
struct Functor< T,typename T :: Vec> {
void operator()()const {
std :: cerr<< 特殊<< std :: endl;
}
};

struct Foo {
typedef std :: vector< int> Vec;
};

int main(){
Functor< Foo> ac;
ac();
}

如何修复它,以便自动使用专门的结构?注意我不想直接专门化 Functor 结构在 Foo ,但我想专门化它在所有类型有 Vec 类型。



PS:我使用的是g ++ 4.4.4

解决方案

很抱歉,在最后一个答案中误导你,我想了一会儿会更简单。所以我会尽量在这里提供一个完整的解决方案。解决这类问题的一般方法是编写一个 traits 帮助模板,并与 enable_if (C ++ 11,boost或手动执行)决定类别专业化:



特质



方法,不一定是最好的,但是写得简单:

  template< typename T& 
struct has_nested_Vec {
typedef char yes;
typedef char(& no)[2];
template< typename U>
static yes test(typename U :: Vec * p);
template< typename U>
static no test(...);

static const bool value = sizeof(test< T>(0))== sizeof(yes);
};

方法很简单,提供两个模板函数,返回不同大小的类型。其中一个采用嵌套的 Vec 类型,另一个采用省略号。对于所有具有嵌套 Vec 的类型,第一个重载是更好的匹配(省略号是任何类型的最差匹配)。对于没有嵌套 Vec 的那些类型,SFINAE将丢弃该重载,并且留下的唯一选项将是省略号。所以现在我们有一个特性来询问是否任何类型都有嵌套的 Vec 类型。



启用if



您可以从任何图书馆使用,或者您可以自己翻译,这很简单:

 模板< bool state,typename T = void> 
struct enable_if {};

template< typename T>
struct enable_if< true,T> {
typedef T type;
};

当第一个参数为 false 基本模板是唯一的选项,并且没有嵌套的类型,如果条件为 true ,则 enable_if 有一个可以用于SFINAE的嵌套类型



实现



现在,我们需要提供模板和专门化,只使用嵌套 Vec

  template< class T,class V = void> 
struct Functor {
void operator()()const {
std :: cerr<< 一般< std :: endl;
}
};
template< class T>
struct Functor< T,typename enable_if< has_nested_Vec< T> :: value> :: type> {
void operator()()const {
std :: cerr<< 特殊<< std :: endl;
}
};

每当我们用类型实例化 Functor 编译器将尝试使用专门化,这将反过来实例化 has_nested_Vec 并获得真值,传递到 enable_if 。对于值为 false enable_if 的类型,没有嵌套的类型类型,因此专门化将在SFINAE中丢弃,并使用基本模板。



/ p>

在你的特定情况下,似乎你不需要专门化整个类型,而只是运算符,你可以将三个元素混合成一个:基于 Vec 的存在分派到两个内部模板函数之一的函子 c> c $ c> enable_if 和traits类:

  template< typename T> 
class Functor {
template< typename U>
void op_impl(typename U :: Vec * p)const {
std :: cout< 专门;
}
template< typename U>
void op_impl(...)const {
std :: cout< 一般;
}
public:
void operator()()const {
op_impl< T>(0);
}
};


I have an SFINAE problem:

In the following code, I want the C++ compiler to pick the specialized functor and print "special", but it's printing "general" instead.

#include <iostream>
#include <vector>

template<class T, class V = void>
struct Functor {
  void operator()() const {
    std::cerr << "general" << std::endl;
  }
};

template<class T>
struct Functor<T, typename T::Vec> {
  void operator()() const {
    std::cerr << "special" << std::endl;
  }
};

struct Foo {
  typedef std::vector<int> Vec;
};

int main() {
  Functor<Foo> ac;
  ac();
}

How can I fix it so that the specialized struct is used automatically? Note I don't want to directly specialize the Functor struct on Foo, but I want to specialize it on all types that have a Vec type.

P.S.: I am using g++ 4.4.4

解决方案

Sorry for misleading you in the last answer, I thought for a moment that it would be simpler. So I will try to provide a complete solution here. The general approach to solve this type of problems is to write a traits helper template and use it together with enable_if (either C++11, boost or manual implementation) to decide a class specialization:

Trait

A simple approach, not necessarily the best, but simple to write would be:

template <typename T>
struct has_nested_Vec {
    typedef char yes;
    typedef char (&no)[2];
    template <typename U>
    static yes test( typename U::Vec* p );
    template <typename U>
    static no test( ... );

    static const bool value = sizeof( test<T>(0) ) == sizeof(yes);
};

The approach is simple, provide two template functions, that return types of different sizes. One of which takes the nested Vec type and the other takes ellipsis. For all those types that have a nested Vec the first overload is a better match (ellipsis is the worst match for any type). For those types that don't have a nested Vec SFINAE will discard that overload and the only option left will be the ellipsis. So now we have a trait to ask whether any type has a nested Vec type.

Enable if

You can use this from any library, or you can roll your own, it is quite simple:

template <bool state, typename T = void>
struct enable_if {};

template <typename T>
struct enable_if<true,T> {
    typedef T type;
};

When the first argument is false, the base template is the only option, and that does not have a nested type, if the condition is true, then enable_if has a nested type that we can use with SFINAE.

Implementation

Now we need to provide the template and the specialization that will use SFINAE for only those types with a nested Vec:

template<class T, class V = void>
struct Functor {
    void operator()() const {
        std::cerr << "general" << std::endl;
    }
};
template<class T>
struct Functor<T, typename enable_if<has_nested_Vec<T>::value>::type > {
    void operator()() const {
        std::cerr << "special" << std::endl;
    }
};

Whenever we instantiate Functor with a type, the compiler will try to use the specialization, which will in turn instantiate has_nested_Vec and obtain a truth value, passed to enable_if. For those types for which the value is false, enable_if does not have a nested type type, so the specialization will be discarded in SFINAE and the base template will be used.

Your particular case

In your particular case, where it seems that you don't really need to specialize the whole type but just the operator, you can mix the three elements into a single one: a Functor that dispatches to one of two internal templated functions based on the presence of Vec, removing the need for enable_if and the traits class:

template <typename T>
class Functor {
   template <typename U>
   void op_impl( typename U::Vec* p ) const {
      std::cout << "specialized";
   }
   template <typename U>
   void op_impl( ... ) const {
      std::cout << "general";
   }
public:
   void operator()() const {
      op_impl<T>(0);
   }
};

这篇关于SFINAE:编译器不选择专门的模板类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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