指向成员函数的指针奇怪

Pointer to member function weirdness

本文关键字:指针 函数 成员      更新时间:2023-10-16

我想做一些绑定与我的c++代码,并使用指针到成员函数。

我有以下代码:

class A
{
explicit A(float);
}
class B
{
void setA(A);
void setA(float);
}
然后声明指向成员函数的指针:
(void (B::*)(A))&B::setA
(void (B::*)(float))&B::setA

编译器(MSVC11)发现第二行有歧义。

如果我在类B中注释了setA(A),编译器认为这两行都是ok的(!)

是编译器错误吗?

在不修改类B的签名的情况下,是否有一种方法可以绕过它?编辑:

实际上,我发布的代码是对我的实际类的过度简化,并且确实编译…

这是一个修改后的版本,它确实再现了这个错误:

class A
{
public:
    explicit A(float f=1.0f, float g=1.0f) {}
};
class B
{
public:
    void setA(A){}
    void setA(float f, float g=1.0f){}
};

(void (B::*)(A))&B::setA
(void (B::*)(float))&B::setA
(void (B::*)(float,float))&B::setA

第二行出现编译错误:错误C2440: '类型转换':无法将'重载函数'转换为'void (__thiscall B::*)(float)'

我认为这是一个bug。c++ 11标准第13.4/1段:

使用没有实参的重载函数名在某些上下文中被解析为函数A指向函数的指针或指向成员函数的重载集中特定函数的指针。[…]。选择的函数类型与上下文中所要求的目标类型的函数类型相同的。[…目标可以是

—对象或引用被初始化(8.5,8.5.3),

—赋值操作(5.17)的左侧,

——函数的形参(5.2.2),

—用户定义操作符(13.5)的形参,

—函数、操作符函数或转换(6.6.3)的返回值,

- 一个显式的类型转换(5.2.3 5.2.9, 5.4),或

—非类型模板形参(14.3.2)。

由于重载集中哪个成员函数的签名与显式将其强制转换为的成员函数的签名相同,因此您的代码是合法的。

除此之外,你的代码在Clang 3.2, GCC 4.7.2, GCC 4.8, ICC 13.0.1和(!)用于。请看下面的例子:

编辑:

你发布的新代码确实是非法的。

第二次强制转换无法解析,因为setA()成员函数中没有一个只接受一个 float形参。因此,编译器不知道表达式&B::setA指的是哪个函数。通常,它会尝试基于上下文显式转换来消除歧义,但这没有帮助,因为它指定了一个与setA()的两个重载的签名不兼容的签名。

如果你想知道为什么会出现这种情况,为什么没有选择第二个重载,那么原因是带有默认实参的形参仍然是函数的形式形参(即使在某些调用中可以省略它),并且它的类型仍然算作函数签名的一部分。

另一方面,默认实参不是函数签名的一部分:

1.3.20 [defns.signature.member]

<class member function>名称,参数类型列表(8.3.5),函数所属的类,cv-qualifiers(如有)和ref-qualifier(如有)

现在,如果去掉重载setA(A),编译器不知道表达式&B::setA引用的是哪个成员函数,因为只有一个。不需要使用显式强制转换来解析表达式。

然后,由于函数指针可以强制转换为其他类型的函数指针,编译器执行额外的转换到指定的目标类型