2016-03-05 39 views
8

考慮下面的代碼片段:從屬名稱查找在函數模板:鏗鏘拒絕,GCC接受

struct X { }; 

namespace foo { 
    template <class T> 
    void bar() { T{} < T{}; } 

    void operator<(const X&, const X&) {} 
} 

int main() { 
    foo::bar<X>(); 
} 

鐺拒絕這段代碼,GCC接受它。這是一個海灣合作委員會的錯誤,或者這是一個鏗鏘蟲?

+0

我不明白它是一個錯誤,因爲'T'類型可以引用任何類型。如果那個類型'T'沒有重載或支持'<'操作符,那麼'bar'函數就會失敗。既然你已經重載了'<'運算符,那就很好。我想這一切都歸結爲編譯器如何讀取代碼,但語義 - 明智的是它不是容易出錯的。 – Poriferous

+0

@Poriferous這沒有任何意義。問題是關於'bar()'的正確行爲是什麼,對於沒有'operator <'的類型'T'。正確的行爲是:「是的,它發現'bar :: operator <'」(在這種情況下,clang有bug)或「代碼格式不正確」(在這種情況下,gcc有bug)。 – Barry

+0

這是沒有道理的,因爲'bar'是一個函數,並且沒有成員'operator <'。既然你已經爲struct'X'定義了'operator <',我真的不知道這裏有什麼問題。即使運算符重載嵌入在結構體「X」中,代碼​​仍應該正確編譯。公平地說,它是鏗鏘有bug的,因爲它似乎假定了什麼類型。我的意思是,你是否嘗試用'foo :: bar ()'來替換'foo :: bar ()',看看clang是否會拒絕該代碼? – Poriferous

回答

5

海灣合作委員會是錯誤的鏗鏘是正確的。 GCC吞下無效代碼的事實也在CLANG的兼容性頁面here中提到。

以下列方式查找不合格的名稱。

  1. 編譯器在寫入名稱的範圍內執行不合格的查找。 對於模板來說,這意味着查找是在模板定義的位置完成的,而不是在實例化的位置。由於operator<此時尚未聲明,因此不合格的查找將無法找到它。
  2. 如果名稱被稱爲函數,那麼編譯器也會進行參數相關查找(ADL)。 (有時,不合格的查找可以抑制ADL;更多信息參見[basic.lookup.argdep]第3段)。在ADL中,編譯器查看調用的所有參數的類型。當它找到一個類的類型時,它在該類的名字空間中查找該名稱;結果是它在這些名稱空間中找到的所有聲明,以及來自非限定查找的聲明。但是,編譯器在知道所有參數類型之前不會執行ADL。
6

我相信這是一個海灣合作委員會的bug,提交爲70099。從[temp.dep.res]:

在解決依賴性的名稱,從以下來源的名稱被認爲是:
(1.1) - 聲明是在模板的定義點可見。
(1.2) - 來自實例化上下文(14.6.4.1)和定義上下文中與函數參數類型關聯的名稱空間的聲明。

foo::operator<()不是在模板中定義的點可見的,並且是不相關的命名空間從功能參數(X的關聯的命名空間只是一個全局命名空間::)。所以我認爲海灣合作委員會是錯誤的發現foo::operator<和鏗鏘是拒絕代碼是正確的。