【发布时间】:2012-07-22 01:52:10
【问题描述】:
我有一个模板类 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 指令都将被忽略。
但是当我取消注释 using NA::add_ref 行时,clang 很乐意编译测试。
从实际的角度来看我的例子,你可以认为act 是boost::intrusive_ptr 的方法,add_ref(A*) 是intrusive_ptr_add_ref(CBase*) 和B 是一些模板,派生自基础CBase。
对此我有几个问题:
clang拒绝我的测试程序是正确的,gcc和Comeau不符合标准吗?标准指定这种不切实际的行为(不允许模板类基作为关联的命名空间)是否有原因?
clang是否以3.4.2/3为由错误地接受我的带有using NA::add_ref指令的测试程序?我应该报告错误吗? :)
附:我已阅读 clang Language Compatibility FAQ 并没有在那里找到答案。
【问题讨论】:
-
C++11 模式有帮助吗? C++11 似乎已经澄清了措辞,因为实际上
NB::B<int>是一个类(恰好是模板特化),而不是模板。 (IIUC 的规则是,与模板相关联的命名空间被添加到与类相关联的命名空间中。) -
这不是很脆弱吗?在某些时候,您可能会添加
add_ref的默认实现,它总是比直接基的关联命名空间更好? -
@LucDanton,你是对的。这两点(关于
class和template-id的关联命名空间)都适用于template-id类。 -
@pmr,这确实有点脆弱。虽然我不明白你的评论。没有 ADL,什么都找不到,因为在来自
intrusive_ptr.hpp的调用范围内没有add_ref函数。 -
@NichtVerstehen 对于默认实现,我的意思是:除非在
act函数的范围内有函数add_ref。act()命名空间中的模板可以更好地匹配,并且会被选择而不是与 base 关联的函数。
标签: c++ templates gcc clang argument-dependent-lookup