我很惊讶在g ++的各种采样版本中,以下编译没有错误或警告:
// Adapted from boost::checked_delete()
template <class T> inline void assert_complete()
{
typedef char type_must_be_complete[ sizeof(T) ? 1 : -1 ];
(void) sizeof(type_must_be_complete);
}
class X;
void f()
{
assert_complete<X>();
}
class X {};
int main() {}
如果X
的定义缺失或者在不同的翻译单元中,我确实会收到错误。
但是在上面的程序中,f
的定义不是我模板的单个实例化点吗?在实例化时X
的不完整性是不是语义错误?
(C ++ 03和/或C ++ 11草案)标准是否将此程序称为格式良好,格式错误,格式错误但不需要诊断或未定义的行为?
编辑:@David Rodriguez - dribeas报道clang ++,comeau和Visual Studio 2010也接受类似的代码。
答案 0 :(得分:12)
(我正在等待Alf Steinbach发表回答,但由于他没有这样做,我会发布他在Lounge C ++聊天中提到的参考文献):
标准表示在翻译单元已经翻译后执行模板实例化,以便及时,模板实例化在之后所有非模板化元素已经已被处理。这在第2.2节翻译阶段中描述:
第1-6段定义预处理器工作和基本的文本操作(字符集的转换,文字的连接......)
7 /分隔标记的空白字符不再重要。每个预处理令牌都转换为令牌。 (2.7)。由此产生的标记在语法和语义上进行分析并翻译为翻译单元。
8 /翻译的翻译单元和实例化单元组合如下:检查每个翻译的翻译单元以产生所需的实例化列表。找到所需模板的定义。实现定义是否需要包含这些定义的翻译单元的来源。执行所有必需的实例化以生成实例化单元。 [注意:这些与翻译的翻译单元类似,但不包含对未实例化模板的引用,也不包含模板定义。 - 结束注释]如果任何实例化失败,程序就会格式不正确。
为简洁起见,我删除了一些注意事项。现在重要的一点似乎是代码被翻译而不会在一个步骤中触发模板实例化,然后在稍后的步骤中实例化模板。这反过来意味着如果类型在翻译单元中的任何地方完成,它将在编译器到达实例化时被处理。
免责声明:这似乎是我尝试过显示完全相同行为的所有编译器的一个很好的理由(gcc,clang,comeau,VS 2010),但这只是当及时执行实例化时,它没有明确声明该类型在模板实例化时可能是不完整的。
答案 1 :(得分:6)
此行完成类型:
class X {};
只要在翻译单元的某处完成了类型,就会完成任何早期的不完整实例。
以下是标准[basic.types](3.9第7段)的相关部分:
类类型(例如“类X”)在翻译单元中的某个点可能不完整,稍后会完成;类型“class X”在两个点都是相同的类型。声明的数组对象类型可能是一个不完整类类型的数组,因此不完整;如果稍后在翻译单元中完成类类型,则数组类型变为完整;这两个点的数组类型是相同的类型。声明的数组对象类型可能是一个未知大小的数组,因此在翻译单元中的某一点不完整,稍后会完成;这两个点的数组类型(“T的未知边界数组”和“N T数组”)是不同的类型。指向未知大小数组或由typedef声明定义为未知大小数组的类型的指针类型无法完成。