这个has_member类模板如何工作? [英] How does this has_member class template work?

查看:208
本文介绍了这个has_member类模板如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想了解以下类模板的工作原理(摘自 here ),但我不能正确理解:

  typename类型> 
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 c $ c>,为什么,那么SFINAE被触发,导致第一个 deduce()函数被忽略, / p>




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

  struct A {}; // SFINAE被触发为A 
struct B {void operator()(){}}; //没有为B触发SFINAE
struct C {void operator()(int,int){}}; //没有为C
触发SFINAE c main()
{
std :: cout< std :: boolalpha; //启用true / false而不是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


解决方案


  • BaseMixin 有一个运算符()定义。

  • c $ c>源于类型 BaseMixin ,因此如果类型有一个运算符()然后在上的名称查找Base :: operator()对于 Base * ,而不是调用

  • deduce *

  • 助手< void(BaseMixin :: *)(),& U :: operator()> code>只会实例化& U :: operator()明确解析为 BaseMixin :: operator()

  • 相反,如果 Helper< void(BaseMixin :: *)(),& U :: operator()> 不会实例化,因为类型有自己的 operator()查找& U :: operator()不明确,因此重载返回类型<$ c $



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


2以下步骤定义成员名的查询结果 f



3在类范围 C

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



4如果 C 一个声明 f ,声明集包含 C中声明的 f ,它满足查找发生的语言结构的要求。 [注意:例如,在阐述型类型说明符基本说明符中查找名称将忽略所有非类型声明,而在 nested-name-specifier 中查找名称时会忽略函数,变量和枚举器声明。作为另一示例,在使用声明中查找名称包括类或枚举的声明,该类或枚举通常被同一范围中的该名称的另一声明隐藏。 — end note ]如果生成的声明集不为空,子对象集包含 C 本身,计算完成。

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



6以下步骤定义合并查找结果: (f,B i )的子对象成员中的至少一个的基本类子对象是S(f,C) / em>,或者如果S(f,B )是空的,则 S(f,C) 。相反,如果S(f,C)的子对象成员中的每一个是S(f,B)的子对象成员中的至少一个的基类子对象, sub>),或者如果 S(f,C)为空,则新的 S(f,C) 否则,如果声明集合)和 S(f,C)不同,则合并是不明确的:新的 S(f,C)在随后的合并中,无效的声明集被认为与任何其他声明集不同。

  • 否则,新的 S (f,C)是使用共享声明集和子对象集的并集的查找集。



  • 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
    

    解决方案

    • 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.

    这篇关于这个has_member类模板如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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