这个has_member类模板如何工作?

Naw*_*waz 4 c++ sfinae template-meta-programming

我试图理解下面的类模板是如何工作的(取自这里),但我无法理解它:

template <typename Type> 
class has_member 
{ 
   class yes { char m;}; 
   class no { yes m[2];}; 
   struct BaseMixin 
   { 
     void operator()(){} 
   }; 
   struct Base : public Type, public BaseMixin {}; 
   template <typename T, T t>  class Helper{}; 
   template <typename U> 
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0); 
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
};
Run Code Online (Sandbox Code Playgroud)

更具体地说,我不了解其中的目的BaseMixin和存在operator().而且,既然Base是源自它,我也不理解它.

更具体地说,当模板参数Type定义时operator(),为什么只有 SFINAE被触发,导致第一个deduce()函数被忽略而第二个函数被选中?


无论如何,这是我的测试代码:

struct A{};                             //SFINAE is triggered for A
struct B{ void operator()(){} };        //SFINAE is not triggered for B
struct C{ void operator()(int,int){} }; //SFINAE is not triggered for C

int main() 
{       
   std::cout << std::boolalpha; //enable true/false instead of 1/0!
   std::cout << has_member<A>::result << std::endl;
   std::cout << has_member<B>::result << std::endl;
   std::cout << has_member<C>::result << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

输出(ideone):

false
true
true
Run Code Online (Sandbox Code Playgroud)

ild*_*arn 8

  • BaseMixin有一个operator()定义.
  • Base派生自两个TypeBaseMixin,所以如果Type有一个operator()名称查找Base::operator()将是不明确的.
  • deduce被要求Base*,而不是Type*.
  • Helper<void (BaseMixin::*)(), &U::operator()>只有在&U::operator()明确解析时才会实例化BaseMixin::operator().
  • 反之,如果Helper<void (BaseMixin::*)(), &U::operator()>没有没有实例,这是因为Type有自己operator()的制作名称查找&U::operator()不明确,因此过载deduce返回类型yes选择.

关于第二个项目的标准引用 - C++11§10.2/ 2-6:

2以下步骤定义f类范围中成员名称的名称查找结果C.

3 查找设置fC被称为S(F,C) ,包括两个部件组:所述声明集,一组命名的成员f; 和子对象集,一组子对象,其中发现了这些成员的声明(可能包括使用声明).在声明集中,using-declaration被它们指定的成员替换,类型声明(包括inject-class-names)被它们指定的类型替换.S(f,C)计算如下:

4如果C包含名称的声明f,该声明集包含的每一个声明f宣布C满足在其中查找出现的语言结构的要求.[ 注意:例如,在elaborated-type-specifierbase-specifier中查找名称时,忽略所有非类型声明,而在嵌套名称说明符中查找名称会忽略函数,变量和枚举器声明.另一个例子是,在using声明中查找名称包括一个类或枚举的声明,该声明通常会被同一范围内该名称的另一个声明隐藏.- 结束注释 ]如果生成的声明集不为空,则子对象集包含C自身,并且计算完成.

5否则(即,C不包含声明f或结果声明集为空),S(f,C)最初为空.如果C具有基类,则计算f每个直接基类子对象B i中的查找集,并将每个这样的查找集S(f,B i)依次合并为S(f,C).

6以下步骤定义将查找集S(f,B i)合并到中间S(f,C)的结果:

  • 如果S(f,B i)的每个子对象成员是S(f,C)的至少一个子对象成员的基类子对象,或者如果S(f,B i)为空,则S(f) ,C)没有变化,合并完成.相反,如果S(f,C)的每个子对象成员是S(f,B i)的至少一个子对象成员的基类子对象,或者如果S(f,C)为空,则新的S(f,C)S(f,B i)的副本.
  • 否则,如果S(f,B i)S(f,C)的声明集不同,则合并是不明确的:新的S(f,C)是具有无效声明集的查找集和子对象集.在后续合并中,无效的声明集被认为与其他任何不同.
  • 否则,新的S(f,C)是具有共享声明集和子对象集的并集的查找集.