6

考虑这段代码:

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

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

编译器似乎非常不同意。(MSVC08/10 说不,GCC<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?
}

这里的关键问题是在 ADL 期间注入的可行函数是否X<1>应该可见X<0>?它们有关联吗?上面提到的所有编译器都接受这个代码,除了 Comeau,它只在宽松模式下接受它。也不确定标准对此有何规定。

你对此有何看法?

4

1 回答 1

4

该标准在14.7.1/4

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

请注意 Vandervoorde 在这里做了一个问题报告,委员会发现

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

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

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

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

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

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

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

于 2010-09-02T22:20:02.203 回答