1

我正在尝试对我的 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),编译器认为这两行都可以(!)

它是编译器错误吗?

有没有办法在不修改 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: 'type cast' :不可能将 'overloaded-function' 转换为 'void (__thiscall B::* )(float)'

4

1 回答 1

1

我会说这是一个错误。根据 C++11 标准的第 13.4/1 段:

使用不带参数的重载函数名称在某些上下文中被解析为函数、指向函数的指针或指向重载集中特定函数的成员函数的指针。[...]。所选函数的类型与上下文所需的目标类型的函数类型相同。[...] 目标可以是

— 正在初始化的对象或引用(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 和 (!) VC10 正常编译。例如,参见这个活生生的例子

编辑:

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

第二个强制转换无法解析,因为没有一个成员函数setA()只接受一个浮点参数。因此,编译器不知道您所说的表达式是哪个函数&B::setA。通常,它会尝试根据上下文显式转换来消除歧义,但这无济于事,因为它指定的签名与setA().

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

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

1.3.20 [defns.signature.member]

签名

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

现在,如果您删除重载setA(A),编译器确实知道表达式&B::setA所指的成员函数,因为只有一个。无需使用显式强制转换来解析表达式。

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

于 2013-04-11T21:36:06.140 回答