8

我有一个从命名空间中NB::B<T>的非模板类派生的模板类NA::A。是在其模板参数的实例上act<T>调用函数的模板函数。add_ref具体来说,act<NB::B<int>>想使用 ADL查找add_ref定义在 's base 的命名空间中。NB::B完整的例子如下:

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 …</p>

— 如果 T 是模板 ID,则其关联的命名空间和类是定义模板的命名空间;对于成员模板,成员模板的类;与为模板类型参数(不包括模板模板参数)提供的模板参数的类型相关联的命名空间和类;定义任何模板模板参数的命名空间;以及定义用作模板模板参数的任何成员模板的类。

令人惊讶的是,模板的基础并未列为关联命名空间的路径。因此clang的行为似乎是正确的。并且正在接受不正确的程序Comeaugcc

同时,参数命名空间中的 '3.4.2/3声明using无效:

在考虑关联命名空间时,查找与将关联命名空间用作限定符 (3.4.3.2) 时执行的查找相同,除了:

— 相关命名空间中的任何 using 指令都将被忽略。

但是当我取消注释时,该using NA::add_refclang很高兴编译测试。

把我的例子放到实际的角度来看,你可以认为那act是一个方法boost::intrusive_ptradd_ref(A*)曾经intrusive_ptr_add_ref(CBase*)B曾经是一些模板,从 base 派生CBase

关于这个我有几个问题:

  1. 拒绝我clang的测试程序gcc并且Comeau不遵循标准是正确的吗?

  2. 标准指定这种不切实际的行为(不允许模板类基作为关联的命名空间)是否有原因?

  3. clang基于以下原因接受我的测试程序与using NA::add_ref指令是错误的3.4.2/3吗?

  4. 我应该报告错误吗?:)

PS 我已阅读clang Language Compatibility FAQ并没有在那里找到答案。

4

1 回答 1

6

从 n3337 开始,它基本上是 C++11,有少量编辑更改,3.4.2/2 内容如下:

对于函数调用中的每个参数类型 T [...] 命名空间和类的集合按以下方式确定:[...]

  • 如果 T 是类类型(包括联合),则其关联的类是:类本身;它所属的类别(如有的话);及其直接和间接基类。其关联名称空间是其关联类是其成员的名称空间。此外,如果 T 是类模板特化,...

然后它继续使用您在问题中发布的基本相同的报价。这里的重要区别是此外,这意味着您引用的列表(我省略了)是除了已经提到的命名空间之外,还包括基类所属的命名空间。

  1. gcc和comeau是对的,clang++拒绝代码是错的。

  2. < 不适用 >

  3. Clang++ 在没有using NA::add_ref.

  4. 是的,您可能应该报告一个错误。它似乎已经被报告和修复。

于 2012-07-23T19:37:52.447 回答