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

我真的需要为常量对象实现用户提供的构造函数吗?

贾沛
2023-03-14

我有密码:

class A {
  public:
    A() = default;

  private:
    int i = 1;
};

int main() {
  const A a;
  return 0;
}

它在g上编译良好(参见ideone),但在clang上失败并出错:

常量类型为“const A”的对象的默认初始化需要用户提供的默认构造函数

我在LLVM bug tracker上报告了这个问题,发现它无效。

我认为试图说服clang开发者是毫无意义的。另一方面,我看不出这种限制的原因。

如果C 11标准以某种方式暗示该代码无效,有人能提出建议吗?或者我应该向g报告一个bug吗?或者,语言规则中有足够的自由度,可以在许多方面处理这段代码?

共有3个答案

石超
2023-03-14

编辑:以下内容基于过时的信息。我刚刚浏览了N3797,我发现:

§8.5/7[dcl.init]
如果程序调用常量限定类型T的对象的默认初始化,则T应为具有用户提供的默认构造函数的类类型

请注意,下面链接中的标准引号表示“用户声明”。

以下程序以g编译,但不以clang编译:

struct A {};

void f()
{
  A const a;
}

这可能与这个bug报告有关,它是“修复的”。g一旦包含数据成员,就无法编译它,除非对其进行初始化。请注意,int member=1将不再成为POD。相比之下,clang拒绝所有置换(空类和数据成员是否初始化)以下段落对标准含义的解释:

§8.5/9[dcl.init]规定:

如果没有为对象指定初始值设定项,并且该对象是(可能是cv限定的)非POD类类型(或其数组),则该对象应默认初始化;如果对象是const限定类型,则底层类类型应具有用户声明的默认构造函数。否则,如果没有为对象指定初始值设定项,则该对象及其子对象(如果有)具有不确定的初始值;如果对象或其任何子对象属于常量限定类型,则程序的格式不正确。

请参阅为什么C需要用户提供的默认构造函数来默认构造常量对象?。如果对象是const-qualified-POD类型,并且没有指定初始值设定项(因为POD不是默认初始化的),则假设程序的格式不正确 注意g在以下情况下的行为:

struct A {int a;};
struct B {int a = 1;};
int main() 
{
    A a;
    B b;
    const A c; // A is POD, error
    const B d; // B is not POD, contains data member initializer, no error
}
夹谷英杰
2023-03-14

请注意,您可以轻松地将类转换为具有用户定义的默认构造函数的类:

class A {
  public:
    A();

  private:
    int i = 1;
};

inline A::A() = default;

根据8.4.2[dcl.fct.def.default]第4段:

... 如果特殊成员函数是用户声明的,并且在第一次声明时未显式默认或删除,则它是用户提供的。。。

这隐含地表明,在第一次声明中未显式默认的函数不是用户提供的。结合8.5[dcl.init]第6段

...如果程序调用const限定类型T的对象的默认初始化,T应是具有用户提供的默认构造函数的类类型。

显然,您不能使用在第一个声明中默认的默认构造函数来初始化const对象。但是,如果不是上面代码中的第一个声明,则可以使用默认定义。

易祖鹤
2023-03-14

N3797§8.5/7规定:

如果程序调用const限定类型T的对象的默认初始化,则T应是具有用户提供的默认构造函数的类类型。

没有进一步的例子或解释。我同意这看起来很奇怪。此外,该规则在C 11中更新为比在C 03中更严格,当时类类型需要用户声明的构造函数。(您的构造函数是用户声明的。)

解决方法是使用{}请求值初始化,或使用Dietmar聪明的类外内联定义。

如果您添加了另一个没有初始值设定项的成员,GCC确实提供了一个诊断(这是一个很好的诊断,指的是较新的C 11要求)。

  private:
    int i = 1;
    int j;
unmem.cpp:11:11: error: uninitialized const ‘a’ [-fpermissive]
   const A a;
           ^
unmem.cpp:1:7: note: ‘const class A’ has no user-provided default constructor
 class A {
       ^
unmem.cpp:3:5: note: constructor is not user-provided because it is explicitly defaulted in the class body
     A() = default;
     ^
unmem.cpp:7:9: note: and the implicitly-defined constructor does not initialize ‘int A::j’
     int j;

GCC源代码引用DR 253,为什么必须初始化空或完全初始化的const对象?这是标准中的一个开放问题,最后一次更新于2011年8月(后C 11),注意:

如果隐式默认构造函数初始化所有子对象,则不需要初始化程序。

因此,尽管Clang符合C 11(并将与C 14保持一致),但GCC正在实施标准化委员会的最新思想。

提交了GCC错误。我预测,当(以及如果)错误得到修复时,您将需要进行诊断。

 类似资料:
  • 问题内容: 当未定义构造函数时,它工作正常,但如果我定义了一个参数化的构造函数而不是默认的构造函数, 并且在创建对象时未传递任何值, 则会出错。我认为构造函数是预定义的。 如果定义了参数化构造函数,为什么需要定义默认构造函数?默认构造函数不是预定义的吗? 问题答案: 仅 当您自己未定义任何构造函数时, 才会 自动创建默认(无参数)构造函数。 如果需要两个构造函数,一个带有参数,另一个不带参数,则需

  • 问题内容: 这对您来说可能听起来很愚蠢,但是为什么我需要在s中定义一个空的构造函数? 我看到的每个教程都说:每个实体都需要一个空的构造函数。 但是Java总是给您一个默认的不可见的空构造函数(如果您不重新定义一个的话)。 谢谢。 我认为这是一个语义问题。我所理解的“需求”是书面的。 含义:始终在您的实体中编写一个空的构造函数。 例: 但是,当您不重新定义Java时,Java总是会为您提供一个空的构

  • 在此处输入图像描述 在此处输入图像描述 我仍然不知道该怎么办(我在UserRepository上尝试了Repository注释-错误是一样的)。错误消息:`启动ApplicationContext时出错。要显示条件报告,请在启用“调试”的情况下重新运行应用程序。2022-03-14 09:04:54.626错误7236---[main]o.s.b.d.LoggingFailureAnalysisR

  • 我知道,如果类实现了外部化,它应该有无参数的构造函数,但是如果类没有任何构造函数(就像我的类),java会为它提供空的无参数的构造函数。那为什么我有个错误呢?如果将无参数构造函数()显式添加到类,则一切都将正常。 错误: 线程“main”java.io.InvalidClassException中的异常:a;java.base/java.io.ObjectStreamClass$Exception

  • 问题内容: 在上面的代码中,我有一个简单的类和一个类级别的实例,也有一个具有相同名称的本地实例。运行上面的代码时,出现以下异常: 问题答案: 您的main方法创建一个实例(),该实例导致实例变量()的初始化,并创建另一个实例,依此类推… 您有无限的构造函数调用链,从而导致。 在上面的代码中,我有一个简单的类和一个类级别的实例 您没有课程级别的实例。您有一个实例级别的实例。如果要一个类级别的实例,请

  • 问题内容: 除了* 使用如下所示的反射检查 之外 ,是否有任何方法 要求 类具有默认(无参数)构造函数?(以下方法可以工作,但是很hacky,反射速度很慢) * 问题答案: 您可以为此构建一个注释处理器。注释处理器是在编译时运行的编译器插件。它们的错误显示为编译器错误,甚至可能停止构建。 这是一个示例代码(尽管我没有运行它): 如果引入注释(例如RequiresDefaultAnnotation)