struct A;
template class Foo { };
Foo foo; // note A is incomplete here
struct A {};
Foo 仅取决于 A 的名称而不是其完整类型 .
所以这是良好的形式;但是,这种事情仍然会破坏(变得格式不好),但在您测试的每个编译器中都会编译 .
首先,我们偷了is_complete . 然后我们这样做:
struct A;
template class Foo {
enum{ value = is_complete::value };
};
Foo foo; // note A is incomplete here
struct A {};
尽管如此,我们还可以:
[...]对于在翻译单元内具有实例化点的任何此类专业化,翻译单元的末尾也被视为实例化点 . [...]
因为该子句不适用于模板类 . 这里,模板类的唯一实例化很好 .
现在,如果在另一个文件中,你有:
struct A {};
Foo foo2;
你的程序生病了 .
但是,在单文件的情况下:
struct A;
template class Foo {
enum{ value = is_complete::value };
};
Foo foo; // note A is incomplete here
struct A {};
Foo foo2; // ill-formed
你的代码很好 . 在给定的编译单元中, Foo 有一个实例化点;第二个是对第一个实例化的引用 .
一个和两个文件的versoins几乎肯定会在C编译器中编译,没有错误或警告 .
有些编译器会记住模板实例化,甚至从一个编译单元到另一个编译单元; Foo 即使在创建 foo2 之后也会 ::value 为 false (带有完整的 A ) . 其他人在每个编译单元中都有两个不同的 Foo ;它的方法将被标记为内联(并且是不同的),类的大小可能不同意,并且您将获得错误的程序问题 .
最后,请注意 std 中的许多类型要求它们的模板参数在旧版本的C中完整(包括c++11:“17.6.4.8其他函数(...)2 . 在以下情况下效果未定义:( ... )特别是 - 如果使用不完整类型(3.9)实例化模板组件时的模板参数,除非特别允许该组件“ - 从boost不完整的容器文档中复制” . 具体而言, std::vector 曾经要求 T 完整 .
[vector.overview] / 3
如果分配器满足分配器完整性要求17.6.3.5.1,则在实例化向量时可以使用不完整类型T.在引用向量特化的任何成员之前,T应该是完整的 .
现在,甚至在c++17之前,大多数 std::vector 的实现都没有用 T 完成,直到你尝试使用一个方法(包括它的许多构造函数或析构函数),但标准声明 T 必须完整 .
这实际上妨碍了一些无用的代码,比如有一个函数类型返回它自己的type1的向量 . Boost有一个库来解决这个问题 .
template
struct Foo {
Foo() {
new T;
}
};
Foo::Foo() 的主体仅被实例化"when called" . 所以 T 缺乏完成没有影响,直到 Foo::Foo() 被调用 .
Foo foo;
^^将无法使用非完整 A 进行编译 .
using foo_t = Foo;
^^将编译,并且不会导致任何问题 .
using foo_t = Foo;
struct A {};
foo_t foo;
也没问题 . 当我们尝试构造 foo_t 时, foo_t::foo_t 的主体被实例化,并且所有定义都匹配 .
1你能说状态机转换功能吗?