我正在尝试使用我的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 casting':无法将'overloaded-function'转换为'void(__thiscall B :: *)(float)'
答案 0 :(得分:1)
我会说这是一个错误。根据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和(!)VC10进行编译。例如,请参阅this live example。
修改强>
您发布的新代码确实是非法的。
无法解析第二个强制转换,因为没有成员函数setA()
只接受一个 float参数。因此,编译器不知道表达式&B::setA
的含义。通常,它会尝试基于上下文显式转换消除歧义,但这没有帮助,因为它指定的签名与setA()
的两个重载的签名不兼容。
如果您想知道为什么会出现这种情况以及为什么没有选择第二个重载,那么原因是带有默认参数的参数仍然是函数的形式参数(即使在某些调用中可以省略它) ),它的类型仍然是函数签名的一部分。
另一方面,默认参数不是函数签名的一部分:
1.3.20 [defns.signature.member]
<强>签名强>
<class member function>
名称,参数类型列表(8.3.5),函数所属的类, cv - 限定符(如果有)和 ref-qualifier (如果有的话)
现在,如果删除重载setA(A)
,编译器会知道表达式&B::setA
所引用的成员函数,因为只有一个。无需使用显式强制转换来解析表达式。
然后,由于函数指针可以转换为其他类型的函数指针,因此编译器会执行对指定目标类型的额外转换。