不完整类型的新内容在包装在模板中时编译

new of incomplete type compiles when wrapped in template

本文关键字:编译 包装 类型 新内容      更新时间:2023-10-16

考虑这段代码,有一个明显的编译错误:(1)

struct A;
struct B {
  B() { new A(); } // error: allocation of incomplete type 'A'
};

使用unique_ptr也无济于事:(2)

struct A;
struct B {
  B() { std::make_unique<A>(); } // error: due to ~unique_ptr()
};

然后(令我非常惊讶)我发现,这将编译:(3)

struct A;
struct B {
  B() { std::make_unique<A>(); }
};
struct A {}; // OK, when a definition is added **below**

然后我检查了一下,这是否也有助于new - :(4)

struct A;
struct B {
  B() { new A(); } // error: allocation of incomplete type 'A'
};
struct A {};

我认为这与template有关,事实上:将new包装在template中确实可以编译:(5)

template <typename T> 
T* my_new() { return new T(); } // OK, when wrapped in template
struct A;
struct B {
  B() { my_new<A>(); }
};
struct A {};

为了完整起见,删除A的定义会再次引发错误:(6)

template <typename T> 
T* my_new() { return new T(); } // error: allocation of incomplete type 'A'
struct A;
struct B {
  B() { my_new<A>(); }
}; 
// do note: definition of A removed

这是怎么回事?据我了解,编译器必须知道分配它的A的大小/定义,因此仅仅声明它是不够的。此外,我认为,定义必须先于分配。

直接使用new时,这似乎是正确的(1,4)。但是当new被包裹时,很明显我错了(2,3,5,6)。

到目前为止,我发现的可能解释是:

  • 对已完成类型的检查将延迟到发生template实例化。我认为这是正确的,但就我而言,直接使用new A()和调用my_new<A>()几乎发生在同一个位置。所以这不能成为原因。右?
  • 使用不完整的类型作为template参数可能是未定义的行为。这是真的吗?即使启用所有警告,编译器也不会抱怨。比较 5 和 6 似乎也表明,编译器足够聪明,可以弄清楚,定义如下(因此实际上使类型完整)。

为什么 4 被认为是不正确的,而 5 编译(或者 5 只是虚假编译未定义的行为 [但 3 也必须有缺陷,对吧?

顺便说一句:使用 CLANG++-3.5.0 和 G++-4.9.2 进行测试

§14.6.4.1 [temp.point]/p1,8,强调我的:

1 对于函数模板专用化,成员函数模板 专用化,或成员函数或静态的专用化 类模板的数据成员(如果专用化是隐式的) 实例化,因为它是从另一个模板中引用的 专业化及其引用上下文取决于 模板参数,专业化的实例化点 是封闭专用化的实例化点。 否则,这种专业化的实例化点 紧跟在命名空间范围声明或定义之后 指专业化。

8 函数模板

、成员函数模板的专用化, 或成员函数或静态数据的成员类模板可以 在一个翻译单元中有多个实例化点,以及 除了上述实例化点外,对于任何 这种专业化在 翻译单元,翻译单元的末尾也被认为是一个 实例化点。类模板的专用化在 翻译单元中的大多数实例化点。一个 任何模板的专用化都可以在 多个翻译单元。如果两个不同的实例化点 根据模板赋予模板专业化不同的含义 定义规则(3.2),程序格式不正确,无诊断 必填。

my_new<A>的实例化有两个点,一个在B定义的末尾,一个在翻译单元的末尾。由于这两点将导致不同的含义(对于片段 3 和 5),因此程序的 NDR 格式不正确(即,它具有未定义的行为)。