2013-04-11 36 views
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级组A(A),这两条线被认为是由编译器OK(!)

它是一个编译器错误?

有没有办法规避,在不修改B类的签名?

编辑:

其实,我贴的代码是从我真正的类过于简单,也编译..

下面是修改后的版本,真正再现BUG:

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)'

回答

1

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

不带参数的用途重载函数名的在某些情况下被解析为一个函数,一个 函数指针或成员函数指针用于超载设置的特定功能。 [...]。 选择 的功能是,其类型为相同在上下文所需的目标类型的功能类型的一个。 [...]的目标可以是

- 一个对象或参考正被初始化(8.5,8.5.3),

- 赋值(5.17)的左侧,

- 一个参数用户定义的运算符(13.5)的参数,

- - 函数(5.2.2),

的函数的返回值,操作者功能,或转化(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 - 限定符(如果有)和裁判限定符(如果有的话)

现在,如果你删除过载setA(A),编译器知道什么成员函数的表达式&B::setA是指,因为只有一个。不需要使用显式类型转换来解析表达式。

然后,由于函数指针可以转换为其他类型的函数指针,因此编译器会对指定的目标类型执行额外的转换。

+0

好的,但是,那么为什么代码编译,如果setA(A)不存在?编译器是否过于自由? – Mikarnage 2013-04-11 22:19:19

+0

@Mikarnage:哦,看起来我太过于肤浅了。我会尝试纠正答案。对不起, – 2013-04-11 22:20:37

+0

@Mikarnage:我编辑过。希望澄清一下 – 2013-04-11 22:23:35