[英]C++0x confusion with using declarations
這種情況會發生什么:
struct A {
void f();
};
struct B : virtual A {
using A::f;
};
struct C : virtual A {
using A::f;
};
struct D : B, C {
void g() {
f();
}
};
感興趣的線是f()
。 顯然,根據FDIS的10.2
查找f
成功並找到A::f
。 但是,哪些候選人會考慮重載決議? 規范在13.3.1p4
說:
對於由using聲明引入到派生類中的非轉換函數,該函數被認為是派生類的成員,用於定義隱式對象參數的類型。
這樣做的目的是,對於單個類,如果這樣的類包含自己的成員函數和使用聲明將基類函數的名稱放入范圍,則在重載解析期間,所有候選函數在其隱式對象中具有相同的類類型參數。 但這對於上面的例子意味着什么呢? 候選人是否會如下?
void F1(B&)
void F2(C&)
// call arguments: (lvalue D)
這似乎是錯誤的,因為根據10.2p7
,我們在查找結果集中只有一個聲明。 我們該如何解讀?
我認為,由於10.2 / 7產生的查找集只產生一個聲明,所以根本沒有函數重載。 13.3.1 / 4僅適用於10.2 / 7產生的查找集包含兩個或多個聲明的情況。
編輯:也許我不像我希望的那樣清晰。 即使f
在A
過載,我認為大多數相同的推理都適用。 也許最好一步一步地采取行動。 (注意,在這種情況下,我使用相同的S(f,X)表示法作為標准,但由於你的派生類最多是D,你的S(f,D)對應於它們的S(f,C)和你的S(f,B)和S(f,C)對應於它的S(f,B 1 )和S(f,B 2 )。
首先s(f,D)是空的,因為我們沒有直接包含在D中的f的聲明。基於此,我們得到10.2 / 5。
在10.2 / 6中,我們首先將s(f,B)合並為S(f,D)。 由於s(f,D)當前為空,我們遵循第一個項目符號點下的第二個條件,S(f,D)成為S(f,B)的副本。
然后我們必須將S(f,C)合並為S(f,D)。 在這種情況下,S(f,C)的每個子對象成員是S(f,D)的子對象成員。 這滿足第一個項目符號點的第一個條件,因此我們保持S(f,D)不變,並且合並完成。
那時,沒有更多的基類B i要考慮,所以我們的S(f,D)= S(f,B)。 的聲明沒有從S(F,C)存在於設定在所有的最終過載。
然后,如果S(f,B)包含兩個或更多個函數,我們進入13.3.1,並解決重載集 - 但由於整個集合來自B,因此問題中假定的情況根本不存在。
只有猜測,完全不確定。 :)
[ Example:
struct A { int x; }; // S(x,A) = { { A::x }, { A } }
struct B { float x; }; // S(x,B) = { { B::x }, { B } }
struct C: public A, public B { }; // S(x,C) = { invalid, { A in C, B in C } }
struct D: public virtual C { }; // S(x,D) = S(x,C)
struct E: public virtual C { char x; }; // S(x,E) = { { E::x }, { E } }
struct F: public D, public E { }; // S(x,F) = S(x,E)
int main() {
F f;
f.x = 0; // OK, lookup finds E::x
}
S(x, F) is unambiguous because the A and B base subobjects of D are also base subobjects of E, so S(x,D)
is discarded in the first merge step. —end example ]
是10.2p7的示例,其中S(f,C)
表示查找集。 最后提供的句子是必不可少的:由於D
和E
都具有相同的C
基類,並且E::x
隱藏了來自該C
的x
,因此最終使用F::x
unambigiuous。
現在,在你的例子中,沒有任何東西隱藏D
的基類的f
,所以D::f
的使用仍然是ambigiuous,我無法看到10.2p7如何適用於你的情況。 就像在頂部說的那樣,完全不確定。 ;)
而不是直接解決問題,我將試圖爭辯說假裝在每個派生類中存在f
函數不起作用:
只有一個候選函數,它有類型
void A::(void)
雖然你可以用這個函數形成一個指向成員的指針
void (A::*F0)(void) = &A::f;
void (B::*F1)(void) = F0;
void (C::*F2)(void) = F0;
這是因為指向成員的指針包含計算函數參數所需的附加信息。 指向成員的指針調用站點找到實際目標實例的A
子對象,以提供f
的this
指針。 函數內部沒有邏輯可以從派生類型的this
指針中找到A的成員。 所以你不能像你的問題所說的那樣談論void F1(B* this)
和void F2(C* this)
。
如果函數被認為是派生類的成員,則它是
void B::A::f(void);
void C::A::f(void);
但是由於B::A
和C::A
是相同的基類,最終候選列表中只有一個函數,盡管它在列表中兩次。 然后虛擬繼承提供兩個候選者在同一個對象上調用相同的函數,沒有歧義。
我認為關鍵是在10.2p5中,標准是指檢查每個“直接基類子對象”。
因為A
是虛擬繼承的,所以它是D
(10.1p4)的“直接基類子對象”。
然后考慮D
三個子對象: A
, B
和C
通過10.2p6 B
和C
被消除( A
是這些的基礎),並且只有A::f
是候選者。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.