【问题标题】:Why is a constructor necessary in a const member struct?为什么在 const 成员结构中需要构造函数?
【发布时间】:2014-07-24 20:47:20
【问题描述】:

我有一个类似这样的代码:

class AClass {
public:
  struct AStruct { };

  AClass(){}

private:
  const AStruct m_struct;
};

int main() {
  AClass a;
}

它会抛出这个编译错误(ClangLLVM 5.1 版):

error: constructor for 'AClass' must explicitly initialize 
       the const member 'm_struct'

如果我为 struct AStruct 指定 C++11 默认构造函数,我会得到同样的错误:

  struct AStruct {
    AStruct() = default;
  };

但是,这可以通过编写一个空主体的构造函数来解决:

  struct AStruct {
    AStruct(){}  // fixed
  };

为什么我需要指定一个空的构造函数?它不是自动创建的,对结构有公共访问权限吗?

为什么C++11默认构造函数没有解决问题?

【问题讨论】:

标签: c++ c++11


【解决方案1】:

来自 §8.5 [dcl.init]/7:

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

AClass 的默认构造函数默认初始化const 成员(见下文),因此该成员必须具有用户提供的默认构造函数。使用 = default 不会产生用户提供的默认构造函数,如 §8.4.2 [dcl.fct.def.default]/4 中所示:

一个函数是用户提供的,如果它是用户声明的并且没有显式默认或者 在第一次声明时删除。


根据 §12.6.2 [class.base.init]/8 对成员进行默认初始化:

在非委托构造函数中,如果给定的非静态数据成员或基类不是由 mem-initializer-id 指定的(包括由于构造函数没有 ctor 而没有 mem-initializer-list 的情况-initializer) 并且实体不是抽象类(10.4)的虚拟基类,则

—如果实体是具有大括号或等号初始化器的非静态数据成员,则实体按照 8.5 中的规定进行初始化;
— 否则,如果实体是匿名联合或变体成员 (9.5),则不执行初始化;
否则,实体默认初始化 (8.5)。

【讨论】:

  • @chris 我使用 G++-4.8。没有错误。我什至可以毫无错误地调用struct A 的const 方法。那么,这个问题是特定的吗?
  • Even worse 这段代码实际上做了一些事情(并且有用),但是我们得到了一个编译器错误。 @PengZhang G++4.8 这里违反了标准,不是clang。
  • @chris,为了增加这个怪癖,如果 =default 在类声明之外定义,那么它被视为用户定义的构造函数,并编译 (8.4.2/5) in the sample, a fails, b compiles
  • @Niall,我实际上处理了那个 :) 如果一个函数是用户声明的并且没有显式默认或删除 第一次声明,则该函数是用户提供的b>.
【解决方案2】:

从@chris 的回答中窃取,我们有这段:§8.5 [dcl.init]/7:

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

然后我们可以构建一个完全荒谬的案例来说明这个限制:

struct Foo {};
int main() {
  const Foo f;
}

按照标准的规定,它无法在 clang 中编译。您的代码就是这样,但作为另一个类/结构的成员变量。

我们甚至可以这样做:

struct Foo {int x = 3;};
int main() {
  const Foo f;
}

显然所有数据都已初始化。最后一个例子让我相信这是标准中的一个缺陷。

这个想法可能与未初始化的 POD 类型和 const 有关,但措辞阻止了不相关的代码。现代 C++ 中的默认构造函数通常绰绰有余,而强制使用 Foo(){} 是一种糟糕的形式。

【讨论】:

    猜你喜欢
    • 2017-01-10
    • 2018-09-06
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 2020-03-10
    • 1970-01-01
    相关资源
    最近更新 更多