这段代码肯定是格式错误的,因为 Foo
在实例化点之后是专门的:
template <typename T>
struct Foo {
int a;
};
Foo<int> x = { 42 };
template <>
struct Foo<int> {
const char *a;
};
Foo<int> x = { "bar" };
由于standard的一部分我强调它是形成不良的:
函数模板,成员函数模板或类模板的成员函数或静态数据成员的特化可以在翻译单元中具有多个实例化点,并且除了上述实例化的点之外,对于任何实例 . 在翻译单元内具有实例化点的这种专门化,翻译单元的末尾也被认为是实例化的点 . 类模板的专门化在翻译单元中最多只有一个实例化点 . 任何模板的专门化可以在多个翻译单元中具有实例化点 . 如果两个不同的实例化点根据单定义规则给出模板特化不同的含义,则程序形成错误,不需要诊断 .
现在, this 代码是不正确的?
struct A;
template <typename> class Foo { };
Foo<A> foo; // note A is incomplete here
struct A {};
如果 Foo
这样声明,那么这种不良形式是否会改变?
struct A;
template <typename T>
struct Foo {
Foo() {
new T;
}
};
Foo<A> foo; // note A is incomplete here
struct A {};
我问了这个问题,因为在这个问题下讨论了这个问题 .
请注意,这不是重复 . 这个问题是关于代码编译的原因,这个问题是关于它是否是不正确的 . 它们不同,因为不正确的程序不一定是非编译程序 .
注意,使用clang和gcc,我的示例使用 new T
编译,而此示例( T
作为成员)不会:
struct A;
template <typename T>
struct Foo {
T t;
};
Foo<A> foo; // note A is incomplete here
struct A {};
也许两者都是不正确的,只有最后一种情况才会给出诊断?
3 回答
假设我们只有一个翻译单元,[temp.point]排除了你的引用作为不良形成的可能来源
相反,第一个代码段的问题是[temp.expl.spec]
第二个片段格式正确,不要求模板参数需要具有完整类型 .
第三个片段格式错误,
new T
要求T
是完整类型 . 这里的一个小问题是构造函数的定义是在Foo<A> foo;
隐式实例化的 . 但是,如果片段更改为然后,构造函数的定义不会被实例化,因此格式正确 . [temp.inst]
第四个片段格式不正确,因为成员需要有完整的类型 . [class.mem]
Foo<A>
仅取决于A
的名称而不是其完整类型 .所以这是良好的形式;但是,这种事情仍然会破坏(变得格式不好),但在您测试的每个编译器中都会编译 .
首先,我们偷了is_complete . 然后我们这样做:
尽管如此,我们还可以:
因为该子句不适用于模板类 . 这里,模板类的唯一实例化很好 .
现在,如果在另一个文件中,你有:
你的程序生病了 .
但是,在单文件的情况下:
你的代码很好 . 在给定的编译单元中,
Foo<A>
有一个实例化点;第二个是对第一个实例化的引用 .一个和两个文件的versoins几乎肯定会在C编译器中编译,没有错误或警告 .
有些编译器会记住模板实例化,甚至从一个编译单元到另一个编译单元;
Foo<A>
即使在创建foo2
之后也会::value
为false
(带有完整的A
) . 其他人在每个编译单元中都有两个不同的Foo<A>
;它的方法将被标记为内联(并且是不同的),类的大小可能不同意,并且您将获得错误的程序问题 .最后,请注意
std
中的许多类型要求它们的模板参数在旧版本的C中完整(包括c++11:“17.6.4.8其他函数(...)2 . 在以下情况下效果未定义:( ... )特别是 - 如果使用不完整类型(3.9)实例化模板组件时的模板参数,除非特别允许该组件“ - 从boost不完整的容器文档中复制” . 具体而言,std::vector<T>
曾经要求T
完整 .由c++17有changed for std::vector:
[vector.overview] / 3
现在,甚至在c++17之前,大多数
std::vector<T>
的实现都没有用T
完成,直到你尝试使用一个方法(包括它的许多构造函数或析构函数),但标准声明T
必须完整 .这实际上妨碍了一些无用的代码,比如有一个函数类型返回它自己的type1的向量 . Boost有一个库来解决这个问题 .
Foo<T>::Foo()
的主体仅被实例化"when called" . 所以T
缺乏完成没有影响,直到Foo::Foo()
被调用 .^^将无法使用非完整
A
进行编译 .^^将编译,并且不会导致任何问题 .
也没问题 . 当我们尝试构造
foo_t
时,foo_t::foo_t
的主体被实例化,并且所有定义都匹配 .1你能说状态机转换功能吗?
幸运的是,这是明确定义的 . 出于同样的原因,这是明确定义的:
这是不正确的: