[英]GCC and ADL for operators in expressions
考慮這個代碼示例
template <typename T> struct S { T t; };
template <class T> void foo(const S<T> &v)
{
bar(v.t);
}
namespace N
{
struct A {};
}
void bar(const N::A &a) {}
int main()
{
S<N::A> a;
foo(a);
}
代碼無法在GCC和Clang中編譯,因為常規查找和ADL都無法解析來自foo
bar
調用。 這是完全可以預料到的,因為bar
調用的關聯命名空間列表只是N
不包括全局命名空間,找不到全局bar
。 一切都應該如此。
但是,如果我將其更改為
template <typename T> struct S { T t; };
template <class T> void foo(const S<T> &v)
{
+v.t;
}
namespace N
{
struct A {};
}
void operator +(const N::A& a) {}
int main()
{
S<N::A> a;
foo(a);
}
它突然開始在GCC中成功編譯。 (同時,Clang拒絕兩個版本的代碼)。
似乎在代碼的第二個(基於運營商的)版本中,GCC也將全局命名空間視為ADL的關聯命名空間。
如果在后一版本的代碼中我將調用更改為
template <class T> void foo(const S<T> &v)
{
operator +(v.t);
}
它將再次無法在GCC編譯。 因此,似乎特別針對運算符表達式表示法給出了某種特殊處理,而不是函數調用表示法。
這個行為標准呢? 我似乎沒有在文檔的文本中找到它(搜索“關聯命名空間”),雖然我依稀記得讀到有關GCC這種特性的一些內容。
這是gcc bug 51577 。 第二個測試用例幾乎就是你的代碼示例。
對於在全局命名空間中查找的運算符查找,沒有特殊規則。 [over.match.oper] / 3有:
根據通常的非限定函數調用([basic.lookup.argdep]中的名稱查找規則),非成員候選集合是表達式上下文中
operator@
的非限定查找的結果,除了所有成員函數都是忽略。
非限定函數調用中通常的名稱查找規則不包括全局命名空間: [basic.lookup.argdep] / 2 :
如果
T
是類類型(包括聯合),則其關聯的類是:類本身; 它所屬的成員,如果有的話; 及其直接和間接基類。 其關聯的命名空間是其關聯類的最內部封閉命名空間。
N::A
是一個類類型,它的關聯類本身,其關聯的命名空間是最內層的封閉命名空間,它只是N
,而不是::
。
這個錯誤報告似乎與Bug 70099有關。 查找中不考慮運算符的名稱空間。
運算符是從屬名稱[temp.dep]/1.3
:
如果運算符的操作數是類型相關的表達式,則運算符也表示從屬名稱。 這些名稱是未綁定的,並且在模板定義的上下文和實例化點的上下文中都會查看模板實例化(17.6.4.1)
並通過[temp.dep.res]
在解析依賴名稱時,將考慮以下來源的名稱:1。在模板定義時可見的聲明。 2.來自名稱空間的聲明,這些名稱空間與實例化上下文(17.6.4.1)和定義上下文中的函數參數的類型相關聯。
並且運算符的聲明既不在上下文中,也不在N::A
的相關名稱空間中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.