是的,你的代码是invalid!这是一个有趣的展示,展示了模板如何以微妙的方式改变代码的含义。下面的代码is valid:
class List
{
public:
class a {
typedef int type;
friend class b; // that's fine!
};
template <typename U > class b;
};
class b {
List::a::type an_int; // allowed to access private member
};
标准表示为 7.3.1.2/3
如果非本地类中的友元声明首先声明了一个类或函数83),则该友元类或函数是最内层封闭命名空间的成员。
什么时候是“首次宣布的班级”?那里也这么说
当查找声明为友元的类或函数的先前声明时,并且当友元类或函数的名称既不是限定名称也不是模板 ID 时,不考虑最内部封闭命名空间范围之外的范围。
“class b”的查找从 7.1.5.3/2 委托给 3.4.4,3.4.4 又委托给 3.4/7 处的非限定名称查找。现在的所有问题是模板名称“b”在友元声明类 a 中是否可见。如果不是,则找不到该名称,并且友元声明将引用全局范围内新声明的类。 3.3.6/1关于它的范围说
类中声明的名称的潜在范围不仅包括以下声明区域
名称的声明符,还有所有函数体、默认参数和构造函数 ctor-
该类中的初始值设定项(包括嵌套类中的此类内容)。
忽略一些迂腐的观点,这些观点可能会使此措辞不适用于此处(这是一个缺陷,但在该段落的 C++0x 版本中已修复,这也使其更易于阅读),此列表不包括友元声明,因为该模板名称可见的区域。
However,友元是在类模板的成员类中声明的。当成员类被实例化时不同的查找适用 - 查找在类模板中声明的朋友名称!标准说
可以在类模板中声明友元类或函数。当模板被实例化时,
它的朋友的名字被视为好像专业化已在其实例化点明确声明。
所以下面的代码是无效的:
template<typename T>
class List
{
public:
class a {
typedef int type;
friend class b; // that's fine!
};
template <typename U > class b;
};
// POI
List<int>::a x;
当这导致List<int>::a
要隐式实例化,名称a
查找“// POI”,就好像已经声明了显式专业化一样。在这种情况下,模板List::b
已经声明了,并且此查找将命中它并发出错误,因为它是模板而不是非模板类。