为什么C++需要用户提供的默认构造函数来默认构造一个const对象?

Kar*_*aru 96 c++

C++标准(第8.5节)说:

如果程序要求对const限定类型T的对象进行默认初始化,则T应为具有用户提供的默认构造函数的类类型.

为什么?在这种情况下,我无法想到为什么需要用户提供的构造函数.

struct B{
  B():x(42){}
  int doSomeStuff() const{return x;}
  int x;
};

struct A{
  A(){}//other than "because the standard says so", why is this line required?

  B b;//not required for this example, just to illustrate
      //how this situation isn't totally useless
};

int main(){
  const A a;
}
Run Code Online (Sandbox Code Playgroud)

Naw*_*waz 66

原因是如果类没有用户定义的构造函数,那么它可以是POD,并且默认情况下不会初始化POD类.那么如果你声明一个未初始化的POD的const对象,它有什么用呢?所以我认为标准强制执行此规则,以便该对象实际上可以有用.

struct POD
{
  int i;
};

POD p1; //uninitialized - but don't worry we can assign some value later on!
p1.i = 10; //assign some value later on!

POD p2 = POD(); //initialized

const POD p3 = POD(); //initialized 

const POD p4; //uninitialized  - error - as we cannot change it later on!
Run Code Online (Sandbox Code Playgroud)

但是如果你让这个课成为非POD:

struct nonPOD_A
{
    nonPOD_A() {} //this makes non-POD
};

nonPOD_A a1; //initialized 
const nonPOD_A a2; //initialized 
Run Code Online (Sandbox Code Playgroud)

注意POD和非POD之间的区别.

用户定义的构造函数是使类非POD的一种方法.有几种方法可以做到这一点.

struct nonPOD_B
{
    virtual void f() {} //virtual function make it non-POD
};

nonPOD_B b1; //initialized 
const nonPOD_B b2; //initialized 
Run Code Online (Sandbox Code Playgroud)

注意nonPOD_B没有定义用户定义的构造函数.编译它.它将编译:

并注释虚函数,然后按预期给出错误:


嗯,我想,你误解了这段话.它首先说明了这一点(§8.5/ 9):

如果一个对象被指定没有初始化,并且该对象是(可能CV修饰)的非POD类型(或其阵列),对象应是缺省初始化; [...]

它讨论了非POD类可能是cv限定类型.也就是说,如果没有指定初始化程序,则应对非POD对象进行默认初始化.什么是默认初始化?对于非POD,规范说(§8.5/ 5),

默认初始化T类型的对象意味着:
- 如果T是非POD类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);

它简单地讨论了T的默认构造函数,无论其用户定义的还是编译器生成的都是无关紧要的.

如果您对此有所了解,那么请理解下一个规范的内容((§8.5/ 9),

[...] 如果对象是const限定类型,则底层类类型应具有用户声明的默认构造函数.

因此,这意味着文本,程序会形成不良的,如果对象是const限定的 POD类型,没有指定的(因为POD没有默认初始化)初始化:

POD p1; //uninitialized - can be useful - hence allowed
const POD p2; //uninitialized - never useful  - hence not allowed - error
Run Code Online (Sandbox Code Playgroud)

顺便说一下,这个编译很好,因为它的非POD,可以默认初始化.

  • 你的ideone链接似乎被打破了,如果这个答案可以更新到C++ 11/14,那将是很好的,因为§8.5根本没有提到POD. (2认同)

Luc*_*ton 11

纯粹的猜测,但我认为其他类型也有类似的限制:

int main()
{
    const int i; // invalid
}
Run Code Online (Sandbox Code Playgroud)

因此,这个规则不仅一致,而且(递归地)也可以防止单元化const(子)对象:

struct X {
    int j;
};
struct A {
    int i;
    X x;
}

int main()
{
    const A a; // a.i and a.x.j in unitialized states!
}
Run Code Online (Sandbox Code Playgroud)

至于问题(允许它类型的默认构造函数)的另一边,我认为这个想法是与用户提供的默认构造函数类型应该总是在施工后部分通情达理的状态.请注意,规则允许以下内容:

struct A {
    explicit
    A(int i): initialized(true), i(i) {} // valued constructor

    A(): initialized(false) {}

    bool initialized;
    int i;
};

const A a; // class invariant set up for the object
           // yet we didn't pay the cost of initializing a.i
Run Code Online (Sandbox Code Playgroud)

那么也许我们可以制定一个规则,比如"至少有一个成员必须在用户提供的默认构造函数中进行明智的初始化",但这样做花费了太多时间来防范Murphy.C++倾向于在某些方面信任程序员.

  • @arne:唯一的问题是它是错误的程序员.试图实例化该类的人可能会给出他想要的所有想法,但他们可能无法修改该类.类作者考虑了成员,发现它们都是由隐式默认构造函数初始化的,所以从来没有添加过. (4认同)
  • 我从标准的这一部分中得到的是"总是为非POD类型声明一个默认构造函数,以防有人想要在某一天创建一个const实例".这似乎有点矫枉过正. (3认同)
  • 好吧,至少通过强迫程序员添加构造函数,他不得不花一分钟时间思考这个问题,并且可能想出一个非平凡的问题. (2认同)

Dav*_*one 9

这被认为是一个缺陷(针对所有版本的标准),并由核心工作组(CWG)缺陷253解决.该标准的新措辞见http://eel.is/c++draft/dcl.init#7

如果T的默认初始化将调用用户提供的T的构造函数(不是从基类继承)或者如果是,则类型T是const-default-constructible

  • T的每个直接非变量非静态数据成员M具有默认成员初始值设定项,或者如果M是类型X(或其数组),则X是const-default-constructible,
  • 如果T是具有至少一个非静态数据成员的并集,则只有一个变体成员具有默认成员初始值设定项,
  • 如果T不是联合,则对于具有至少一个非静态数据成员(如果有)的每个匿名联合成员,只有一个非静态数据成员具有默认成员初始化程序,并且
  • 每个可能构造的T的基类都是const-default-constructible.

如果程序要求对const限定类型T的对象进行默认初始化,则T应为const-default-constructible类类型或其数组.

这个措辞本质上意味着明显的代码有效.如果你初始化所有的基础和成员,你可以说A const a;不管你是否拼写任何构造函数.

struct A {
};
A const a;
Run Code Online (Sandbox Code Playgroud)

自4.6.4以来,gcc已经接受了这一点.clang从3.9.0开始就接受了这一点.Visual Studio也接受这一点(至少在2017年,不确定是否更快).

  • 但这仍然禁止 `struct A { int n; A() = 默认值;}; const A a;` 同时允许 `struct B { int n; B() {} }; const B b;` 因为新的措辞仍然说“用户提供”而不是“用户声明”,我不明白为什么委员会选择从这个 DR 中排除明确默认的默认构造函数,迫使我们制作我们的如果我们想要具有未初始化成员的 const 对象,则类是非平凡的。 (2认同)