2013-04-11 31 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