N4296::14.7.1/1 [temp.inst]
提供了以下示例:
template<class T, class U>
struct Outer {
template<class X, class Y> struct Inner;
template<class Y> struct Inner<T, Y>; // #1a
template<class Y> struct Inner<T, Y> { }; // #1b; OK: valid redeclaration of #1a
template<class Y> struct Inner<U, Y> { }; // #2
};
Outer<int, int> outer; // error at #2
并给出以下解释:
Outer<int, int>::Inner<int, Y>
已在#1b
重新申报。 (它不是 已定义,但注明与...中的定义相关联Outer<T, U>
。)#2
也是#1a
的重新声明。它被注意到 与定义相关联,因此它是无效的重新声明 部分专业化。
我对#1b
被视为声明但不是定义这一事实感到困惑。我们在那里提供了函数体,为什么它仍然没有定义?事实上,你无法解释这种解释。
答案 0 :(得分:3)
这在示例前面的文字中有解释!
类模板特化的隐式实例化会导致类成员函数,成员类,作用域成员枚举,静态数据成员和成员模板的声明的隐式实例化,而不是定义或默认参数的隐式实例化。它会导致隐式实例化未作用域成员枚举和成员匿名联合的定义。但是,为了根据9.2确定成员的实例化重新声明是否有效,与模板中的定义相对应的声明被认为是 定义
实例化模板所产生的函数定义与函数模板本身的定义之间存在差异。
答案 1 :(得分:0)
名称通过其第一个声明引入其作用域,然后与实体(如对象,函数或类)相关联。在某些范围(例如命名空间范围或类范围)中,可以多次声明名称。名称声明还可以包括命名实体的定义。在看到定义之后,据说有关的声明与定义相关联。命名实体只允许使用一个定义。
当隐式地实例化类模板时,其嵌套函数,类和静态对象成员的定义不会立即实例化。它们只在需要时才被实例化。但是,有一个特殊的规则来捕捉潜在的定义冲突。
在此示例中,在包含两个参数T
和U
的类模板中,声明了具有两个参数X
和Y
的内部类模板。为内部类定义了两个部分特化,一个用于X
与T
重合的情况,另一个用于X
与U
重合的情况。第一个部分特化首先在#1a声明,然后重新声明并与#1b的定义相关联。声明第二个部分特化并与#2的定义相关联。
到目前为止一切顺利。
但如果T
和U
属于同一类型,例如int
,该怎么办?在这种情况下,对于任何给定的Y
,#1a,#1b和#2中的声明都声明了相同的名称Outer<int, int>::Inner<int, Y>
。其中两个声明与定义相关,并导致冲突。您引用的N4296中的示例是为了证明即使没有需要实例化Outer<int, int>::Inner<int, Y>
的引用,也必须诊断冲突。
Outer<int, char>
很好,因为在这种情况下T
和U
不一致,因此#2给出的Outer<int, char>::Inner<char, Y>
定义与Outer<int, char>::Inner<int, Y>
不同在#1b中定义。