ADL和朋友注射

时间:2010-09-02 22:12:23

标签: c++ templates friend-function argument-dependent-lookup

考虑以下代码:

template <int N>
struct X
{
 friend void f(X *) {}
};

int main()
{
 f((X<0> *)0); // Error?
}

编译器似乎非常不同意。 (MSVC08 / 10说不,GCC&lt; 4.5说是,但4.5说不,sun 5.1说是,intel 11.1也说是,但是comeau说不(两者都是EDG))。

根据“C ++模板 - 完整指南”:

  

......假设有一个电话   涉及查找朋友   相关的类实际上导致了   要实例化的类......虽然   这显然是那些人的意图   写了C ++标准,但事实并非如此   在标准中清楚地说明了。

我找不到标准中的相关部分。有没有参考?

考虑这种变化:

template <int N>
struct X
{
 template <int M>
 friend void f(X<M> *) {}
};

template <>
struct X<0>
{
};

int main()
{
 X<1>();
 f((X<0> *)0); // Error?
}

这里的关键问题是X<1>的ADL期间X<0>注入的可行功能是否应该可见?它们是否相关?上面提到的所有编译器都接受此代码,但Comeau只接受放松模式。不确定标准对此有何看法。

你对此有何看法?

1 个答案:

答案 0 :(得分:4)

标准在14.7.1/4

处说
  

如果在需要完全定义的对象类型的上下文中使用类类型,或者如果类类型的完整性影响程序的语义,则隐式实例化类模板特化;特别是,如果表达式的类型是类模板特化涉及重载解析,指针转换,指向成员转换的指​​针,则类模板特化被隐式实例化(3.2);

请注意,Vandervoorde制作了issue report here,委员会找到了

  

标准已经指定这会创建一个实例化点。

对于第二种情况 - 您需要考虑参数f(X<0>*)的关联类和名称空间。这些是,因为这是一个指向类模板特化的指针(注意下面的“template-id”不太正确 - C ++ 0x纠正了使用正确的术语)以及指向类的指针(这个令人困惑的分裂在C ++ 0x中也得到了纠正 - 它在一个子弹点中列出了这两种情况。)

  
      
  • 如果T是模板ID,则其关联的名称空间和类是模板所在的名称空间   定义; [......很多噪音...]

  •   
  • 如果T是类类型(包括联合),则其关联的类是:类本身;它所属的成员,如果有的话;及其直接和间接基类。其关联的名称空间是定义其关联类的名称空间。

  •   

总而言之,我们的关联类是X<0>,关联的命名空间是全局命名空间。现在可见的朋友功能是

  
      
  • 在关联类中声明的任何命名空间范围友元函数在其各自的命名空间中都是可见的,即使它们在普通查找期间不可见
  •   

X<0>中没有声明友元函数,因此在查看全局命名空间时,友元函数声明不可见。请注意,X<0>是一种与X<1>完全不同的类型。你在那里进行X<1>的隐式实例化对这个调用没有影响 - 它只是在引用类X<1>的友元函数的全局命名空间中添加了一个不可见的名称。