2010-09-02 48 views
6

考虑以下代码:ADL和朋友注射

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

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

编译器似乎不同意巨资。 (MSVC08/10说不,GCC < 4.5说是,但4.5说不,太阳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只在轻松模式下接受。不知道该标准对此有何评论。

你对此有何看法?

回答

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>中没有声明朋友函数,因此在查看全局名称空间时,friend函数声明不可见。请注意,X<0>是与X<1>完全不同的类别类型。你在那里做的X<1>的隐式实例对这个调用没有影响 - 它只是在全局名字空间中添加一个不可见的名字,这个名字引用了类X<1>的朋友函数。

+0

第一个例子应该是可编译的吗? – jpalecek 2010-09-02 22:48:20

+0

谢谢:)这或多或少是我怀疑的。那么多编译器如何得到第二个错误的错误?另外,如果您也可以为其他引用的标准提供参考,我会认真的。 – uj2 2010-09-02 22:48:58

+0

@ uj2好了,至少clang与GCC相比看起来不错。它正确地拒绝了第二种情况。特别是海湾合作委员会有相当多的模板一致性问题,所以这并不令我感到意外。你试图找到哪些其他引号标准段落? – 2010-09-02 22:53:51