【问题标题】:Why is my class non default-constructible?为什么我的课程不可默认构造?
【发布时间】:2019-12-09 15:52:15
【问题描述】:

我有这些课程:

#include <type_traits>

template <typename T>
class A {
public:
    static_assert(std::is_default_constructible_v<T>);

};

struct B {
   struct C {
      int i = 0;
   };

    A<C> a_m;
};

int main() {
    A<B::C> a;
}

编译时,a_m 不是默认可构造的,但a 是。

C 更改为:

struct C {
      int i;
   };

一切都很好。

使用 Clang 9.0.0 测试。

【问题讨论】:

  • GCC 8.3 - 好的,GCC 9.1/9.2 - 失败。
  • 使用C() {} 也可以。
  • 这对我来说很臭。在 Bugzilla 上没有立即明显的匹配。
  • 有趣:A 中的static_assert 失败,但如果您改为在A 内默认构造一个T(例如,将成员T t; 放在那里),一切正常.类型特征告诉您的内容与实际可能的内容之间存在不一致...
  • @Nicolas 是的,但这是因为一些边缘情况,这些情况都不适用于这里(特别是,正如 cppreference 上的同一句话所说,const int x; 在没有初始化程序的情况下是无效的,纯粹是因为const 以及内置类型的初始化行为和一些历史)

标签: c++ nsdmi


【解决方案1】:

标准文本和 cmets 中指出的几个主要实现都不允许这样做,但出于完全不相关的原因。

首先,“按部就班”的原因:A&lt;C&gt; 的实例化点按照标准是immediately before the definition of B,而std::is_default_constructible&lt;C&gt; 的实例化点就在其之前:

对于类模板特化,[...] 如果特化是 隐式实例化,因为它是从另一个内部引用的 模板特化,如果特化来自的上下文 被引用取决于模板参数,如果 特化没有在实例化之前实例化 封闭模板,实例化点是立即 在封闭模板的实例化点之前。 否则,这种特化的实例化点 紧接在命名空间范围声明或定义之前 那是指专业化。

由于此时C 显然不完整,因此实例化std::is_default_constructible&lt;C&gt; 的行为是未定义的。但是,请参阅 core issue 287,这将更改此规则。


实际上,这与 NSDMI 有关。

  • NSDMI 很奇怪,因为它们会延迟解析 - 或者用标准术语来说,它们是“完整类上下文”。
  • 因此,= 0 原则上可以引用 B 中尚未声明的内容,因此实现无法真正尝试解析它,直到它完成 B
  • 完成一个类需要隐式声明特殊成员函数,尤其是默认构造函数,因为C 没有声明构造函数。
  • 该声明的部分内容(constexpr-ness、noexcept-ness)取决于 NSDMI 的属性。
  • 因此,如果编译器无法解析 NSDMI,则无法完成该类。
  • 结果,在实例化A&lt;C&gt;的那一刻,它认为C是不完整的。

处理延迟解析区域的整个领域都被严重低估,伴随着实现分歧。可能需要一段时间才能清理干净。

【讨论】:

  • 我同意这个答案的第二部分(NSDMI),不确定第一部分(“按书本”的原因)。我认为“实例化点”只影响从属名称查找,但is_default_constructible 不需要查找任何从属名称。我们可以在没有模板的情况下观察到同样的问题:godbolt.org/z/TGqYbd
  • @einpoklum 这只是这个问题的链接
  • @Oktalist:对不起,我复制粘贴错误。因此,非常感谢您在 this related question 中就 NSDMI 的怪异提供意见。
  • @einpoklum 我同意你的问题的答案。不能仅仅因为错误消息没有说它不完整,就认为该类是完整的。嵌套类的完整性很复杂,并且存在多个开放缺陷。这与此问题中的问题相同。还有stackoverflow.com/q/53408962/1639256
【解决方案2】:

Undefined behavior是:

如果上面的模板的实例依赖,直接或 间接地,在不完整的类型上,并且实例化可能会产生 如果假设完成了该类型,则会出现不同的结果, 行为未定义。

【讨论】:

  • 为什么 C 不完整?
  • @interjay, C 已完成,但 B 未完成。而B::C 间接依赖于B
  • @Evg 文本“直接或间接依赖”仅出现在 cppreference.com 上。标准只是说类型 T 需要是完整的。
  • @T.C.很好的发现,看起来该文本是从 C++17 开始添加的。我不清楚“依赖”另一种类型是什么意思,以及这是否包括类嵌套。
  • @interjay 我写了大部分的文字。我们想说的是 1) 如果您以一种可能在稍后完成某些不完整类型时引发 ODR 违规的方式实例化一个特征,那是未定义的; 2) 即使您没有实际上在您的程序中导致 ODR 违规,它也是未定义的,因此标准库实现可以选择在使用该特征时进行诊断,如果他们愿意的话。如果 C 有一个默认构造函数模板,其中包含一些奇怪的 SFINAE,如果 B 以不同方式完成,则可以更改答案,那么可以肯定,特征取决于它。
猜你喜欢
  • 2019-09-28
  • 1970-01-01
  • 2020-05-08
  • 1970-01-01
  • 2018-10-04
  • 2013-03-20
相关资源
最近更新 更多