我有一個模板類NB::B<T>
從命名空間中的非模板類NA::A
派生。 act<T>
是一個模板函數,在其模板參數的實例上調用add_ref
函數。具體而言,act<NB::B<int>>
想要使用ADL在NB::B
的基地名稱空間中找到add_ref
。完整的例子如下:通過模板類的基的參數依賴查找
template<class T>
void act() {
T* p = 0;
add_ref(p); // the failing line
}
namespace NA
{
struct A { };
// I want ADL to find this:
void add_ref(A* p) {
}
}
namespace NB
{
// template class with non-template base
template <class T>
struct B: NA::A { };
typedef B<int> Bi;
// using NA::add_ref; // fixes the problem
}
int main()
{
act<NB::Bi>();
}
這個編譯好吧gcc
(4.7.0)。並在Comeau
在線。然而clang
(3.1)失敗:
a.cpp:4:3: error: use of undeclared identifier 'add_ref'
與此同時,標準如下:
3.4.2/2 ...
- 如果T是模板id,其相關的名稱空間和類是定義模板的名稱空間;對於成員模板,成員模板的類;與爲模板類型參數提供的模板參數類型(不包括模板模板參數)相關聯的名稱空間和類;在其中定義了任何模板模板參數的名稱空間;以及定義用作模板模板參數的任何成員模板的類。
令人驚訝的是,模板的基地沒有列爲關聯名稱空間的路徑。因此clang
的行爲似乎是正確的。並且Comeau
和gcc
正在接受不正確的程序。
與此同時,3.4.2/3
指出using
的論辯的命名空間沒有任何效果:
在考慮相關的命名空間,查找是一樣的,當相關的名稱空間被用作執行查找一個限定符(3.4.3.2)除了:
- 關聯名稱空間中的任何使用指令都將被忽略。
但是當我取消註釋using NA::add_ref
行clang
很樂意編譯測試。
把我的例子轉化爲現實的角度來看,可以認爲act
是的boost::intrusive_ptr
的方法,add_ref(A*)
是intrusive_ptr_add_ref(CBase*)
和B
是一些模板,從基地CBase
獲得。
這對於我有幾個問題:
我是不是正確的,
clang
的是正確的拒絕我的測試程序,並gcc
和Comeau
不遵循標準?是否有一個原因爲什麼標準指定這種不切實際的行爲(不允許將模板類庫作爲關聯的名稱空間)?
是
clang
錯誤接受我的測試程序與using NA::add_ref
指令的理由3.4.2/3
?我應該報告一個錯誤嗎? :)
P.S.我已閱讀clang Language Compatibility FAQ,但沒有找到答案。
C++ 11模式有幫助嗎? C++ 11似乎已經澄清了措辭,因爲事實上'NB :: B'是一個類(它恰好是模板專業化),而不是模板。 (IIUC的規則是,與類型爲專業化的模板相關聯的名稱空間被*添加到與該類相關聯的命名空間中。) –
2012-07-23 19:39:50
這不會很脆弱嗎?在某些時候,你可能會添加一個默認的'add_ref'實現,它總是比直接基地的相關命名空間更好的匹配? – pmr 2012-07-23 19:40:08
@LucDanton,你是對的。這兩點(關於'class'和'template-id'的關聯名字空間)都適用於類「template-id」。 – 2012-07-24 10:26:41