当前位置: 首页 > 知识库问答 >
问题:

显式默认的constexpr ctor是否允许非constexpr初始化

龚星洲
2023-03-14

我刚刚偶然发现了GCC和Clang之间关于显式默认constexpr ctor和一些继承的以下差异。。。

template <typename T>
struct A {
  constexpr A() = default;
  T v;
};

struct B : A<int> {
  constexpr B() = default;
};

GCC立即拒绝代码,而Clang允许实例化这两种类型的非constexpr版本。我的猜测是,叮当声可能是正确的,但我不是百分之百肯定。。。

共有1个答案

程钧
2023-03-14

问题归结为:如果不使用默认初始化某些内置类型的非静态数据成员的constexpr构造函数,它是否有效?

tl; dr:

>

  • 对于非模板构造函数,不,不初始化任何非静态数据成员是无效的。

    对于模板构造函数,是的,对于实例化的构造函数不满足constexpr构造函数的要求的部分(但不是全部,不需要诊断)实例化模板专门化是有效的。

    在这种情况下,GCC是对的,而Clang是错的。

    GCC给出了以下信息丰富的错误消息

    prog.cc:8:13: error: explicitly defaulted function 'constexpr B::B()' cannot be declared as 'constexpr' because the implicit declaration is not 'constexpr':
        8 |   constexpr B() = default;
          |             ^
    prog.cc:3:13: note: defaulted constructor calls non-'constexpr' 'A<T>::A() [with T = int]'
        3 |   constexpr A() = default;
          |             ^
    prog.cc:3:13: note: 'A<T>::A() [with T = int]' is not usable as a 'constexpr' function because:
    prog.cc:4:5: note: defaulted default constructor does not initialize 'int A<int>::v'
        4 |   T v;
          |     ^
    

    现场演示

    注意,错误出现在B的构造函数上,而不是A的构造函数上,其构造函数只是“不可用作constexpr函数,因为默认的默认构造函数不初始化int A

    根据[dcl.constexpr]/4:

    constexpr构造函数的定义应满足以下要求:

    • 该类不应具有任何虚拟基类

    此外,其功能体应为删除,或应满足以下要求:

    • [...]
    • 每个非变体非静态数据成员和基类子对象都应初始化([class.base.init]);
    • [...]

    这里,v属于int类型,并且未初始化。因此,似乎不能声明A的构造函数constexpr。

    然而,[dcl.constructor]/6表示:

    如果constexpr函数模板或类模板的成员函数的实例化模板专门化无法满足constexpr函数或constexpr构造函数的要求,则该专门化仍然是constexpr函数或constexpr构造函数,即使对此类函数的调用不能出现在常量表达式中。如果当被视为非模板函数或构造函数时,模板的任何专门化都不能满足constexpr函数或constexpr构造函数的要求,则模板的格式不正确,不需要诊断。

    因此,声明为constexpr的A的构造函数实际上是有效的,即使在为T=int实例化时也是有效的!

    问题是B的构造函数B是一个普通类(与类模板相反),对于其构造函数(仅)被声明为constexpr

    因此,应该像GCC一样拒绝此代码。

    (请注意,两个编译器都拒绝此类类型的初始化,例如:

    A a{};
    B b{};
    

    上述代码被两个编译器拒绝。)

    正如评论中提到的,初始化A::v和GCC(以及标准)会很高兴。

  •  类似资料:
    • 考虑以下代码 VS2013编译器发出以下警告: 警告C4351:新行为:数组“B::member”的元素将默认初始化1 这里有记载 使用C 11,并应用“默认初始化”的概念,意味着B. part的元素将不会被初始化。 但我认为,成员{}应该执行值初始化,而不是默认初始化。VS2013编译器是否损坏? 8.5美元/6 默认初始化类型为T的对象意味着:-如果T是(可能是cv限定的)类类型(第9条),则

    • 问题内容: 题 苹果的文档指定: 首次初始化属性时,不会调用willSet和didSet观察器。仅当在初始化上下文之外设置属性的值时才调用它们。 是否可以强制在初始化期间调用它们? 为什么? 假设我有这堂课 我创建了method ,以使处理调用更简洁,但我只想处理函数中的属性。有没有办法在初始化过程中强制此调用? 更新资料 我决定只为我的类删除便捷的初始化器,并强迫您在初始化后设置属性。这使我知道

    • 我们举个例子: 一个常见的解决方案是转移到一个抽象类,但是在我的具体案例中,我有一个枚举的接口,所以在这里不适用。我想这不是被忽略了,就是因为接口背后的原始想法,即它们是可用方法的“契约”,但我想我需要关于这是怎么回事的输入。 我读过“为什么Java 8接口方法中不允许使用”final“?”,其中说: 默认方法的基本思想是:它是具有默认实现的接口方法,派生类可以提供更具体的实现 与关联问题一样,由

    • 编译器必须为constexpr的不同路径生成多个函数,这取决于函数的调用方式。所以也许它在使用过载分辨率。 被接受的建议没有提到“sfinae”或“过载分辨率”。 所以我很好奇这两个概念是否适用于“constexpr if”。

    • 我在Visual Studio2019中有一个Xamarin表单解决方案。我现在只使用Android版本。完整的错误是: 在此进程中,java.lang.IllegalStateException Message=Default FirebaseApp未初始化AppCardView.Sample.android。确保首先调用FirebaseApp.InitializeApp(上下文)。 在我的An

    • 问题内容: 所以我要声明并初始化一个int数组: 说我改为这样做… … 0将按标准输出。另外,如果我这样做: 将按标准输出。那么默认如何初始化我的数组?是否可以安全地假设默认的初始化将数组索引设置为零,这意味着我不必遍历数组并对其进行初始化? 问题答案: Java程序中未由程序员明确设置为所有内容的所有内容都初始化为零值。 对于的引用(任何包含对象的内容)。 对于int / short / byt