[英]Operator overload resolution work within namespaces
namespace A{
struct A{
inline void operator+(const int B) {}; // 1)
};
inline void operator+(const A& a,const int B) {}; // 2)
}
inline void operator+(const A::A& a,const int B) {}; // 3)
int main()
{
A::A a;
a+1; // compile questions
return 1;
}
以上代碼可以毫無問題地進行編譯。
但是,如果對1)進行了注釋,則由於“在2)和3)中“'a + 1'中'operator +'的重載不明確”而無法編譯。 我可以理解,運算符+是在類中首先搜索到的,這是因為如果1)沒有被注釋,則編譯沒有問題。 我不是嗎
主要問題:如果注釋了1),為什么編譯器找到了一個匹配的運算符+,卻繼續找到其他運算符? 我也很好奇應該首先找到哪個。 (我認為應根據以下信息立即停止操作:運算符重載解析如何在名稱空間中工作? )
第二個問題:
namespace A{
struct A{
inline void operator+(const long int B) {}; // 4), the input parameter type has been changed to long int
};
inline void operator+(const A& a,const int B) {}; // 5)
void test();
}
inline void operator+(const A::A& a,const int B) {}; // 6)
void A::test()
{
A a; // a)
a+ 1;
}
int main()
{
A::A a;
a+1; // b)
return 1;
}
由於4)和5),a)處的編譯歧義錯誤。
由於4),5)和6),b)處的編譯歧義錯誤。
問題i)
對於a)和b),為什么編譯器已經在4)中找到了帶有(const long int輸入參數類型)的operator +,它是結構A中的成員運算符,所以它仍然會繼續尋找其他運算符。 在我看來,編譯器應該停止在那里給出類型錯誤信息。 它應與成員功能的輸入參數類型完全匹配的情況相同,它會停止搜索其他參數。
問題2)
我認為編譯器應在4)停止並給出類型錯誤信息。 如果繼續,為什么成員函數重載函數(long int)與輸入參數完全匹配的非成員具有相同的分辨率等級? 在這種情況下,我認為如果編譯器決定繼續搜索,則輸入參數完全匹配的非成員情況應該會獲勝,這更有意義。
在C ++標准的“ 13.3.1.2表達式中的運算符”部分,第2點中對此進行了說明:
如果任何一個操作數的類型是類或枚舉,則可能會聲明一個用戶定義的運算符函數來實現該運算符,或者可能需要用戶定義的轉換才能將該操作數轉換為適合於在運算符中。
根據進一步的解釋, a+1
將轉換為a.operator+(1)
(成員函數)或operator+(a,1)
(非成員函數)。
第3點說明:
對於二進制運算符@,其左操作數的類型為cv不合格版本為T1,右操作數的類型為cv不合格版本為T2,這三組候選函數包括指定的候選函數,非成員候選函數和內置函數候選人,其構成如下:
- 如果T1是完整的類類型,則候選成員集是對T1 :: operator @進行合格查找的結果
這解釋了為什么在活動狀態下選擇了(1)。 當您將其注釋掉時,候選成員集為空。 然后,根據標准中的下一個項目符號:
- 非成員候選集是在表達式的上下文中根據不合格函數調用中用於名稱查找的常規規則對操作符@進行不合格查找的結果,只是忽略了所有成員函數。
根據這些規則,由於您的表達式位於main()
而不包含在名稱空間中,因此找到了兩個候選函數,當然,這對於編譯器來說是模棱兩可的。
您可以通過main()
的以下語句輕松區分要使用的運算符:
using A::operator+;
如果您在名稱空間A的函數中包含表達式a + 1,那么也不會存在歧義:
//...your code preceding the main()
namespace A{ // reopen the namespace
void f()
{
A a;
a + 1; // version of the namespace is taken first
}
}
只要在名稱空間中定義的運算符將被首先采用(只要(1)仍被注釋掉)。
關於第二個問題
首先說明:如果成員函數像其他兩個函數一樣將int
用作參數,則不會有歧義,並且將與第一個問題一樣為這兩個表達式選擇(4)。 如果所有三個函數都使用long
作為參數而不是int
則會發生同樣的情況。
名稱相同但參數不同的事實要求我們在標准的13.3.1.2節中更深入地研究名稱隱藏和重載規則。 第6點指出:
用於重載解決方案的候選函數集是成員候選者,非成員候選者和內置候選者的並集。 參數列表包含運算符的所有操作數。 根據13.3.2和13.3.3從候選函數集中選擇最佳函數
13.3.2是關於可行的函數的,即具有相同數量的參數以及參數類型與參數類型之間的隱式轉換。
13.3.3關於選擇最佳可行功能。 “ 如果恰好有一個可行的功能比所有其他可行的功能要好,那么它就是由過載解析選擇的功能;否則調用格式不正確 ”。
在(a)的情況下,有兩個最佳可行的函數:在名稱空間(5)中具有轉換的成員函數(4)或沒有轉換的非成員函數(因為test()在名稱空間中)。 因此,模棱兩可。
對於b),存在三個最佳可行功能:與(a)和(6)中相同。 但是請注意,如果(6)將使用long,則不會引起歧義,因為成員函數將獲勝。
解決對函數+的調用,編譯器執行基於參數的查找,其中包括一組候選函數的構造和重載解析。 候選函數包括成員,非成員和內置候選,非成員候選包括通過不合格名稱查找找到的所有聲明。 如果未注釋1,則正常查找合格名稱會找到匹配的類成員函數。 如果注釋為1,則ADL查找與函數參數類型關聯的名稱空間集合。 它找到兩個候選函數,它們都是可行的函數,並且具有相同的隱式轉換序列等級,從而導致模棱兩可的過載錯誤。 此處提供了相同的示例http://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.