Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么我的类是非默认可构造的?_C++ - Fatal编程技术网

C++ 为什么我的类是非默认可构造的?

C++ 为什么我的类是非默认可构造的?,c++,C++,我有这些课程: #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&

我有这些课程:

#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;
}
一切都很好

使用Clang 9.0.0进行测试。

它是:

如果上述模板的实例化依赖于,则直接或 间接地,在一个不完整的类型上,实例化可能会产生 如果假设该类型已完成,则会产生不同的结果 行为是未定义的


标准文本和注释中提到的几个主要实现都不允许这样做,但原因完全无关

首先,“按书”的原因:根据标准,
A
的实例化点是,,
std::is_default_constructible
的实例化点就在该点之前:

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

由于此时
C
显然是不完整的,因此实例化
std::is_default_constructible
的行为是未定义的。但是,请参见,这将改变此规则


实际上,这与NSDMI有关

  • NSDMI很奇怪,因为它们得到了延迟解析——或者用标准说法,它们是“完整的类上下文”
  • 因此,
    =0
    原则上可以引用
    B
    中尚未声明的内容,因此在完成
    B
    之前,实现无法真正尝试解析它
  • 完成类需要隐式声明特殊成员函数,特别是默认构造函数,因为
    C
    没有声明构造函数
  • 该声明的部分内容(constexpr ness、noexcept ness)取决于NSDMI的属性
  • 因此,如果编译器无法解析NSDMI,则无法完成该类
  • 因此,在实例化
    a
    时,它认为
    C
    是不完整的

处理延迟解析区域的整个领域都严重缺乏规范,伴随着实现上的分歧。清理之前可能需要一段时间。

GCC 8.3-好,GCC 9.1/9.2-失败。使用
C(){}
也可以。我觉得这有点问题。Bugzilla上没有立即明显的匹配。有趣的是:
A
中的
static_断言
失败,但是如果您改为默认在
A
内部构造一个
T
(例如,将一个成员
T;
放在那里),它就可以正常工作。类型特征告诉你的和实际可能发生的不一致…@Nicolas True,但这是因为一些边缘情况,这些情况在这里都不适用(特别是,正如cppreference上的同一句话所说,
const int x;
在没有初始化器的情况下是无效的,这纯粹是因为
const
和内置类型的初始化行为以及一些历史记录)为什么C不完整?@interjay,
C
是完整的,但是
B
不是。而且
B::C
间接依赖于
B
@Evg文本“直接或间接依赖”仅出现在cppreference.com上。标准只是说类型T需要完整。@T.C.很好的发现,看起来该文本是从C++17开始添加的。我不清楚“依赖”另一个类型意味着什么,以及它是否包括类嵌套。@interjay我写了大部分这句话。我们想说的是1)如果你以一种方式实例化了一个特征,当某个不完整的类型完成后,可能会引发ODR冲突,这是未定义的;2)它是未定义的,即使您实际上没有在程序中导致ODR冲突,因此标准库实现可以选择在使用该特性时进行诊断,如果他们愿意的话。如果
C
有一个默认的构造函数模板,其中包含一些奇怪的SFINAE,如果
B
的完成方式不同,则可以更改答案,当然,这一特性取决于它。我同意这个答案的第二部分(NSDMI),但不确定第一部分(“按书”的原因)。我认为“实例化点”只会影响依赖名称的查找,但是
是\u default\u constructible
不需要查找任何依赖名称。我们可以在没有模板的情况下观察到相同的问题:
struct C {
      int i;
   };