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

时间:2015-01-24 15:37:39

标签: c++ templates forward-declaration

考虑这个代码,有一个明显的编译错误:(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是否有帮助 - nope :( 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必须也有缺陷,对吧?])?

btw:用clang ++ - 3.5.0和g ++ - 4.9.2

进行测试

1 个答案:

答案 0 :(得分:6)

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

  

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

     

8功能模板的专业化,成员函数模板,   或者类模板的成员函数或静态数据成员可以   在翻译单元中有多个实例化点,和   除了上面描述的实例化点之外,对于任何   这种专业化有一个实例化点   翻译单位,翻译单位的结尾也被认为是   实例化点。类模板的专业化有   翻译单元中的大多数实例化点。一个   任何模板的特化都可能有实例化点   多个翻译单位。 如果有两个不同的实例化点   根据一个给出模板专业化的不同含义   定义规则(3.2),该程序是不正确的,没有诊断   必需的。

my_new<A>有两个实例化点,一个位于B定义的末尾,一个位于翻译单元的末尾。由于这两点会产生不同的含义(对于片段3和5),程序是不正确的NDR(即,它具有未定义的行为)。