SFINAE确定某种类型是否有方法

时间:2016-01-19 06:43:10

标签: c++ c++11 c++14 template-meta-programming

template <typename T>
struct has_xxx {

 private:

  using true_type = char;
  using false_type = long;

  template <typename C>
  static true_type has_xxx_impl(decltype(&C::xxx)); // comment 1

  template <typename C>
  static false_type has_xxx_impl(...); // comment 2

 public:
  enum { value = sizeof(has_xxx_impl<T>(0)) == sizeof(true_type) }; // comment3

};

struct Foo { int xxx() {return 0;}; };
struct Foo2 {};

int main() {
   static_assert(has_xxx<Foo>::value, "");
}

这是一个检测结构是否具有某种方法的结构。 我对代码有一些疑问。

  1. 在评论1中,这&#39;&amp; C&#39;是的,为什么我不能写'C :: xxx&#39;
  2. 在评论2中,参数&#39; ...&#39;是,它是一个参数包,还是它代表任何类型的参数
  3. 在评论3中,has_xxx_impl(0)如何工作? T被Foo取代,那么参数0怎么样?为什么选择第一个功能?

2 个答案:

答案 0 :(得分:1)

以下说明不完善,不引用标准。我尽力解释它是可以理解的。

has_xxx_impl(decltype(&C::xxx))接受由decltype(&C::xxx)生成的类型 &C::xxx是指向成员的指针,decltype(&C::xxx)是其类型。

另一个has_xxx_impl(...)接受“任何”

所以,当你有has_xxx_impl<T>编译器必须选择时,上面的一个。它更喜欢更好的匹配。因此,当类型包含xxx时,第一个匹配比...版本更好。如果它不包含这样的成员,则无法选择该函数,并且必须使用另一个函数(...

感谢SFINAE - 替换失败(尝试传递不存在的成员类型时)不会产生错误(选择其他重载)

请注意,两个函数都返回不同的类型(true_typefalse_type,这些类型被定义为具有不同的大小),因此使用sizeof(has_xxx_impl<T>(0)) == sizeof(true_type)可以判断选择了哪一个。此比较的结果设置为value,您可以从外部访问。

答案 1 :(得分:1)

  

在评论1中,这个'&amp; C'是什么意思,为什么我不能只写'C :: xxx'

&实际上是指xxx的{​​{1}}部分。也就是说,在&C::xxx中获取成员xxx的地址(希望是一个函数,而不是静态成员 - 稍后会更多)

  

在评论2中,参数'...'是什么意思,它是参数包,还是代表任何类型的参数

C或省略号运算符是variadic argument,它接受​​任何内容。这是一种非常C风格的做事方式,通常不是很安全的。今天你最常见的地方是吞咽异常:

...

(注意:这不是参数包,也不是折叠表达式的一部分。请参阅§5.2.2/ 6)

  

在评论3中,has_xxx_impl(0)如何工作? T被Foo取代,那么参数0怎么样?为什么选择第一个函数?

try{ ThrowableFunctionCall(); } catch(...) // swallow any exceptions {} 是一种使用overload resolution将值设置为sizeof(has_xxx_impl<T>(0)) == sizeof(true_type)true_type的聪明方式。请允许我解释一下:

首先调用false_type会使其显示为

has_xxx_impl<T>

是否可行,因为它们具有相同的名称,并且可能都可以使用类型template <typename C> static true_type has_xxx_impl(decltype(&C::xxx)); // comment 1 template <typename C> static false_type has_xxx_impl(...); // comment 2 进行实例化。然而

T

尝试创建指向成员类型的指针(例如,decltype(&C::xxx) 表示成员函数指针),并且它是在类型推导的上下文中这样做的。如果声明格式不正确,那么SFINAE生效并使其成为一个糟糕的候选人,只留下

int(C::*)()

由于template <typename C> static false_type has_xxx_impl(...); // comment 2 匹配任何内容,因此该值设置为...false_type

如果语句格式正确,则可以将long正确分配给函数指针类型(它不情愿地转换为具有0值的指针类型)。此转换优先于匹配nulltry it yourself!),因此值...true_type

最后,

char

我们可以说是将value = sizeof(has_xxx_impl<T>(0)) == sizeof(true_type)的大小与char的大小进行比较(没有long个案例) 或者大小为xxx,大小为long(有long个案例。)

请注意,在C ++ 11/14中完全没有必要这样做,如果xxx是成员变量而不是函数,则确实会失败:

xxx

melak47的评论虽然更简单,却有类似的垮台。

更好的解决方案是使用struct Foo3{static const int xxx;}; 类型特征:

is_member_function_pointer

Live Demo