这个 has_member 类模板如何工作?

发布于 2025-01-02 07:04:38 字数 1627 浏览 3 评论 0原文

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

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

I'm trying to understand how the following class template works (taken from here), but I couldn't understand it properly:

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

More specifically, I don't understand the purpose of BaseMixin and the presence of operator() in it. Also, since Base is derived from it, I don't understand it as well.

Even more specifically, when template parameter Type has defined operator(), why only then SFINAE is triggered, causing the first deduce() function to be ignored and the second one is chosen?


Anyway, this is my test code:

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

Output(ideone):

false
true
true

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

白昼 2025-01-09 07:04:38
  • BaseMixin 有一个 operator() 定义。
  • Base 派生自 TypeBaseMixin,因此如果 Type 有一个 operator() 那么 Base::operator() 上的名称查找将是不明确的。
  • deduce 是针对 Base* 而不是 Type* 调用的。
  • Helper 仅当 &U::operator() 明确解析时才会实例化到BaseMixin::operator()
  • 相反,如果 Helper 没有实例化,这是因为 Type< /code> 有自己的 operator() ,使得 &U::operator() 上的名称查找不明确,因此导致 deduce 的重载返回选择类型yes

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

2 以下步骤定义类作用域 C 中成员名称 f 的名称查找结果。

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

4 如果 C 包含名称 f 的声明,则声明集包含 C< 中声明的 f 的每个声明/code> 满足查找发生的语言结构的要求。 [注意:例如,在详细类型说明符基本说明符中查找名称会忽略所有非类型声明,在嵌套名称说明符中查找名称时会忽略函数、变量和枚举器声明。作为另一个示例,在 using-declaration 中查找名称包括类或枚举的声明,这些声明通常会被同一范围内该名称的另一个声明隐藏。 —结束说明 ] 如果生成的声明集不为空,则子对象集包含 C 本身,计算完成。

5 否则(即 C 不包含 f 声明或生成的声明集为空),S(f,C) 最初是空的。 如果C有基类,计算每个直接基类子对象f的查找集Bi,并将每个此类查找集 S(f,Bi) 依次合并为 S(f,C)

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


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

  • BaseMixin has an operator() definition.
  • Base derives from both Type and BaseMixin, so if Type has an operator() then name lookup on Base::operator() will be ambiguous.
  • deduce is called for Base*, not Type*.
  • Helper<void (BaseMixin::*)(), &U::operator()> will only instantiate if &U::operator() unambiguously resolves to BaseMixin::operator().
  • Conversely, if Helper<void (BaseMixin::*)(), &U::operator()> does not instantiate, it's because Type has its own operator() making name lookup on &U::operator() ambiguous, and consequently the overload of deduce returning type yes is chosen.

Standard citation regarding the second bullet — C++11 §10.2/2-6:

2 The following steps define the result of name lookup for a member name f in a class scope C.

3 The lookup set for f in C, called S(f,C), consists of two component sets: the declaration set, a set of members named f; and the subobject set, a set of subobjects where declarations of these members (possibly including using-declarations) were found. In the declaration set, using-declarations are replaced by the members they designate, and type declarations (including injected-class-names) are replaced by the types they designate. S(f,C) is calculated as follows:

4 If C contains a declaration of the name f, the declaration set contains every declaration of f declared in C that satisfies the requirements of the language construct in which the lookup occurs. [ Note: Looking up a name in an elaborated-type-specifier or base-specifier, for instance, ignores all non-type declarations, while looking up a name in a nested-name-specifier ignores function, variable, and enumerator declarations. As another example, looking up a name in a using-declaration includes the declaration of a class or enumeration that would ordinarily be hidden by another declaration of that name in the same scope. —end note ] If the resulting declaration set is not empty, the subobject set contains C itself, and calculation is complete.

5 Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi, and merge each such lookup set S(f,Bi) in turn into S(f,C).

6 The following steps define the result of merging lookup set S(f,Bi) into the intermediate S(f,C):

  • If each of the subobject members of S(f,Bi) is a base class subobject of at least one of the subobject members of S(f,C), or if S(f,Bi) is empty, S(f,C) is unchanged and the merge is complete. Conversely, if each of the subobject members of S(f,C) is a base class subobject of at least one of the subobject members of S(f,Bi), or if S(f,C) is empty, the new S(f,C) is a copy of S(f,Bi).
  • Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous: the new S(f,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent merges, an invalid declaration set is considered different from any other.
  • Otherwise, the new S(f,C) is a lookup set with the shared set of declarations and the union of the subobject sets.
随心而道 2025-01-09 07:04:38

Base 可以从 Type 继承 operator(),或者从 BaseMixin 继承,如果 Typeoperator() 它隐藏了 BaseMixin 的运算符。

因此,如果 Type 没有定义 operator (),则 Baseoperator() 可以隐式转换为BaseMixinoperator()。然后,用 U==Base 推导(U*, Helper* = 0) code> 将匹配。

相反,如果 Type 定义了 operator(),则 Baseoperator() 无法转换为 BaseMixin 是其中之一,因此 derive(U*, Helper* = 0) 将不匹配。

Base can inherit operator() from Type, or from BaseMixin and if Type has operator() it hides BaseMixin's operator.

So, if Type has no operator () defined, Base's operator() can be casted implicitly to BaseMixin's operator(). Then, deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0) with U==Base will match.

Oppositely, if Type has operator() defined, Base's operator() cannot be casted to BaseMixin's one, so deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0) will not match.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文