枚举和指向成员的指针

时间:2011-03-05 20:12:23

标签: c++ pointers enumeration member

我最近尝试创建一个is_class类,并且需要一种方法让编译器区分枚举类型和定义了转换运算符的类类型。看到类,结构和联合是如何与指针到成员函数兼容的唯一类型,我决定让编译器确定用于实例化is_class模板的类型是否与指针兼容 - 成员函数。在遇到几个问题之后,我决定在与指向成员的指针一起使用时测试枚举的行为并得到一些古怪的结果。以下部分说明了第一个怪癖:

enum ENUM {};
void Test(void (ENUM::*pmem) (void))
{
    /* ... */
}
Test(NULL);

使用Microsoft Visual C ++ 2010进行编译时,函数定义的指向成员的部分:(ENUM::*pmem)

以红色突出显示,鼠标悬停在声明上会显示错误:

Error: "ENUM" is not a class type

但是,编译器会解析此段而不会遇到任何错误,并将pmem分配给NULL。有趣的是,编译器会允许这种看法,因为枚举类型不是类,结构或联合,因此不能拥有自己的方法。

第二个兴趣点出现在创建模板函数时,采用类型不同的指向成员的参数:

template<class _Ty>
void Test_Template(void (_Ty::*pmem) (void))
{
    /* ... */
}

当然,为了使用此功能,必须明确限定:

Test_Template<ENUM>(NULL);

但是,此调用会生成错误说明:

invalid explicit template argument(s) for 'void Test(void (__thiscall _Ty::* )(void))'

我通过创建一个额外的函数模板解决了这个问题,该模板的原型将匹配任何未能匹配前模板函数原型的调用(涉及使用省略号)。

问题:

  1. 为什么枚举与指向成员的指针兼容?

  2. 当编译器为模板Test显式限定生成错误时,为什么在调用非模板Test_Template函数时存在完全匹配?

1 个答案:

答案 0 :(得分:2)

关于你的第一个问题,似乎编译器确实报告枚举不能有成员函数,因为编译器报告了函数声明的错误。它可能通过内部尝试尽可能地纠正错误声明来让调用成功,在这种情况下意味着注意到你试图声明像指针一样的东西并允许调用。没有要求编译器在该行上给出错误;由于程序是由我组建的,只要编译器拒绝使用诊断程序,它就不需要在任何地方给出错误。

关于你的第二个问题,让第二个模板让错误消失的原因是"substitution failure is not an error" (SFINAE) principle。当编译器使用某些类型参数实例化函数模板时,如果它发现特定函数实例化无效(例如,尝试获取指向枚举成员的指针),则它不会报告错误。相反,它只是从考虑中删除该模板。但是,如果在使用给定参数进行实例化时,您编写的模板都不是有效的,那么编译器将发出错误,因为它无法找到您正在尝试执行的操作的匹配项。在第一种情况下,当您只有一个模板时,会发生错误,因为SFINAE会从考虑中消除唯一的候选模板,导致模板即时没有匹配的模板。在第二种情况下,在实例化模板后,“全部捕获”模板仍然有效,因此在排除指向成员指针的模板时,仍然可以引用合法模板。因此,代码非常好。