4

我试图了解以下类模板的工作原理(取自此处),但我无法正确理解:

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))); 
};

更具体地说,我不明白其中的目的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;
}

输出(ideone):

false
true
true
4

2 回答 2

8
  • BaseMixin有一个operator()定义。
  • Base派生自Typeand BaseMixin,所以如果Type有一个operator()then 名称查找Base::operator()将是不明确的。
  • deduce是要求Base*,不是Type*
  • Helper<void (BaseMixin::*)(), &U::operator()>&U::operator()只有在明确解析为时才会实例化BaseMixin::operator()
  • 反之,如果Helper<void (BaseMixin::*)(), &U::operator()>不实例化,那是因为有自己的明确的名称查找,因此选择了返回类型的重载。Typeoperator()&U::operator()deduceyes

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

2 以下步骤定义了f在类范围内对成员名称进行名称查找的结果C

3 in 的查找,称为S(f,C),由两个组件集组成:声明集,一组名为 的成员;和subobject set,一组子对象,其中找到了这些成员的声明(可能包括using-declaration s)。在声明集中,using-declaration被它们指定的成员替换,类型声明(包括注入的类名)被它们指定的类型替换。S(f,C)计算如下:fCf

4 如果C包含 namef的声明,则声明集包含满足查找发生的语言构造要求的每个声明的f声明。C[注意:例如,在详细类型说明符基本说明符中查找名称会忽略所有非类型声明,而在嵌套名称说明符中查找名称会忽略函数、变量和枚举器声明。作为另一个示例,在using-declaration中查找名称包括类或枚举的声明,该类或枚举通常会被同一范围内的该名称的另一个声明隐藏。——尾注] 如果结果声明集不为空,则子对象集包含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)是具有共享声明集和子对象集并集的查找集。
于 2012-02-02T18:23:42.537 回答
2

Base可以继承operator()Type, 或继承自BaseMixin并且如果Typeoperator()隐藏了BaseMixin's 运算符。

因此,如果Type没有operator ()定义,Base'soperator()可以隐式转换为BaseMixin's operator()。然后,deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0)withU==Base将匹配。

相反,如果Typeoperator()定义,则Base'soperator()不能强制转换为BaseMixin'1,因此deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0)将不匹配。

于 2012-02-02T18:28:08.907 回答