当不完整类型被包含在模板中时,新的不完整类型可以编译。

10
考虑以下代码,其中存在明显的编译错误:(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 {};

我猜这与模板有关,事实上:在模板中包装new是可以编译的: (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进行测试


http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html - OmnipotentEntity
如果不清楚的话,因为那篇文章只涉及到了两阶段名称查找中更严格的一侧,模板名称直到代码经过另一次遍历并读取A的定义后才被实例化,这就是为什么它能够编译通过的原因。 - OmnipotentEntity
1
将模板实例化的延迟直到翻译单元的末尾是一种常见的实现技术。您的代码格式不正确,无需进行诊断。 - T.C.
1个回答

6

§14.6.4.1 [temp.point]/p1,8, emphasis mine:

1 对于函数模板特化、成员函数模板特化或类模板的成员函数或静态数据成员的特化,如果该特化由另一个模板特化内部引用并且引用它的上下文依赖于模板参数,则该特化会被隐式实例化,其实例化点是封闭特化的实例化点。否则,对于这种特化,其实例化点紧跟着引用该特化的命名空间范围声明或定义之后。

8 函数模板、成员函数模板以及类模板的成员函数或静态数据成员的特化可能在一个翻译单元中具有多个实例化点,并且除了上述实例化点之外,对于任何在翻译单元中具有实例化点的这种特化,翻译单元的末尾也被视为实例化点。类模板的特化在一个翻译单元中最多只有一个实例化点。任何模板的特化都可以在多个翻译单元中具有实例化点。如果两个不同的实例化点根据一次定义规则(3.2)给出模板特化不同的含义,则程序无效,无需诊断。

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


应该澄清的是,(3)和(5)有不同的含义,(6)在两个点上的含义相同 - 都是无效的。对吗? - Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接