使用者:Redstone1024/優先性規則(C++)
在 C++ 編程語言中,優先性規則是指在涉及繼承的情況下,對類成員的無限定名字查找的一種特殊規則。當編譯器計算一個名字可能對應的聲明集合時,那些來自更遠基類且不如較近基類中的聲明「優先」的聲明,將在名字查找中被隱藏。在其他語言或語境中,同樣的原則可能被稱為「名字屏蔽」或「變量遮蔽」。
計算名字查找的算法在 C++11 標準的 10.2 節 [class.member.lookup] 中描述[1]。標準的描述沒有使用「優先」這個詞,而是傾向於用「查找集合」和「隱藏」來描述事物。但是索引中包含一個引用到第 10.2 節的「優先、虛基類」條目。
非菱形繼承示例
[編輯]void f(double, double); // 全局作用域
struct Grandparent {
void f(int);
void f(double, double);
};
struct Parent : public Grandparent {
void f(int); // 隐藏 Grandparent::f 的所有重载
};
struct Child : public Parent {
void g() { f(2.14, 3.17); } // 解析为 Parent::f
};
在上述例子中, Child::g
包含對名字 f
的引用。然而,然而,整個程序中存在四個 f
的聲明。為了確定究竟指的是哪一個 f
,編譯器會計算一個重載集合,其中包含在調用點未被隱藏的所有 f
的聲明。全局作用域中的 f
被 Grandparent::f
隱藏,而 Grandparent::f
又被 Parent::f
隱藏。因此,重載決議只考慮 Parent::f
這一聲明 — 最終的結果是非良構的,因為調用提供了兩個參數,而 Parent::f
只期望一個參數。
對於新 C++ 程序員而言,通常令他們感到意外的是,基類中 Parent::f
的聲明會優先並隱藏所有來自更遠基類的同名聲明,而不論各自的函數簽名如何;也就是說,儘管這兩個成員函數的參數列表截然不同, Parent::f(int)
的聲明依然優先並隱藏了 Grandparent::f(double, double)
的聲明。
同樣需要注意的是,在 C++ 中,名字查找先於重載決議。如果 Parent::f
擁有多個重載(例如 f(int)
和 f(double, double)
),那麼編譯器會在重載決議階段從它們中進行選擇;但在名字查找階段,我們僅僅需要在 Grandparent::f
、 Parent::f
和 ::f
這三個作用域中做出選擇。事實上, 雖然 Grandparent::f(double, double)
作為重載候選比 f(int)
的重載更好,但這一點並不會被編譯器在名字查找過程中考慮。
菱形繼承示例
[編輯]struct Grandparent {
void f(int);
void f(double, double);
};
struct Mother : public Grandparent {
void f(int); // 隐藏 Mother::Grandparent::f 的所有重载
};
struct Father : public Grandparent { };
struct Child : public Mother, Father { // Mother::Grandparent 与 Father::Grandparent 不是同一个子对象
void g() { f(2.14, 3.17); } // Mother::f 与 Father::Grandparent::f 之间存在歧义
};
在上述示例中,編譯器為 f
構造了一個重載集合,其中既包含了 Mother::f
也包含了 Father::Grandparent::f
。所以編譯器會生成一個診斷信息,表明此程序因為名字 f
歧義而非良構。
虛繼承示例
[編輯]struct Grandparent {
void f(int);
void f(double, double);
};
struct Mother : public virtual Grandparent {
void f(int); // 隐藏 Mother::Grandparent::f 的所有重载
};
struct Father : public virtual Grandparent { };
struct Child : public Mother, Father { // Mother::Grandparent 与 Father::Grandparent 是同一个子对象
void g() { f(2.14, 3.17); } // 解析为 Mother::f
};
在這個最後的例子中,名名字 f
再次毫無歧義地指向 Mother::f
,因為 Mother::f
隱藏了在其 Grandparent
對象中聲明的 f
。標準中提到了這一令人驚訝的情況(§10.2 第 10 段):
即便 Child
自身虛繼承 Grandparent
,也不會在名字查找時引起歧義。然而,如果 Child
從 Grandparent
非虛繼承(即 struct Child : public Mother, Father, Grandparent
),那麼在兩個由 Grandparent
形成的子對象中所聲明的 f
成員之間將再次出現歧義。
外部連結
[編輯]參考文獻
[編輯]- ^ 1.0 1.1 N3797 Working Draft, Standard for Programming Language C++. Dated 2013-10-13.