【问题标题】:Does it violate the standard for a non-default-constuctible struct to lack a user-defined constructor?非默认可构造结构缺少用户定义的构造函数是否违反了标准?
【发布时间】:2015-04-25 00:02:12
【问题描述】:

可以定义一个struct (a) 没有用户定义的构造函数,并且 (b) 不能为其生成默认构造函数。比如下面的Foo

struct Baz
{
   Baz(int) {}
};

struct Foo
{
   int bar;
   Baz baz;
};

您仍然可以使用聚合初始化创建Foo 的实例:

Foo foo = { 0, Baz(0) };

我的普通编译器 (VS2012) 会勉强接受这一点,但它会引发 2 个警告:

警告 C4510:“Foo”:无法生成默认构造函数。

警告 C4610:struct 'Foo' 永远无法实例化 - 需要用户定义的构造函数

当然,我刚刚证明警告 #2 是错误的——您仍然可以使用聚合初始化来实例化它。我尝试过的在线编译器很乐意接受上述内容,所以我猜 VS2012 只是对这个警告过于激进了。但我想确定一下——这段代码是否正常,或者它在技术上是否违反了标准中一些晦涩的部分?

【问题讨论】:

  • 这当然是一个完美的 cromulent 聚合,可以按照 C++11 的描述进行实例化。要么是旧方言中存在一些晦涩难懂的东西(我对此表示怀疑,但无法证实,我的语言历史学家甚至不如语言律师),或者编译器的警告过于严厉。
  • 只是为了摆脱警告,您是否尝试过删除默认构造函数以便编译器不会尝试不可能的事情?
  • 这不是一个没有构造函数的结构。它有一个隐式声明的复制构造函数。
  • @DaleWilson VS2012 不支持删除语法,但如果我确信代码没问题,我可以使用 pragma 禁用警告。
  • @T.C.这是真的(见我的编辑)。我想到的构造函数类型有一个简洁的术语吗?

标签: c++ visual-studio-2012 language-lawyer default-constructor aggregate-initialization


【解决方案1】:

该标准在 [12.1p4] 中明确允许 Foo 之类的情况:

[...] 如果没有用户声明的构造函数 类 X,没有参数的构造函数被隐式声明为默认 [...] 类 X 的默认默认构造函数定义为 在以下情况下删除:

[...]

  • 任何可能构造的子对象,除了带有大括号或等号初始化器的非静态数据成员,都具有类类型 M(或数组 其中) 并且 M 没有默认构造函数 或重载 应用于 M 的默认构造函数的分辨率 (13.3) 会导致 模棱两可或在功能中被删除或无法访问 默认的默认构造函数

[...]

Baz 没有默认构造函数,所以上面强调的部分适用(强调我的)。

对于这种情况,没有什么“未定义”或“格式错误”。隐式声明的默认构造函数被定义为已删除,仅此而已。你可以明确地做同样的事情,它仍然是有效的。

聚合的定义在 [8.5.1p1] 中。对于 C++14,它是:

聚合是没有用户提供的数组或类(第 9 条) 构造函数(12.1),没有私有或受保护的非静态数据成员 (第 11 条),没有基类(第 10 条),没有虚函数 (10.3)。

'no user-provided' 部分允许您在所有可能被隐式声明的构造函数上使用= delete(使它们是用户声明的,但不是用户提供的),并且该类仍然是一个聚合,允许您可以对其使用聚合初始化。

至于警告C4610,我之前和reported it都遇到过。如您所见,它已在即将发布的 VC++ 版本中修复。

值得一提的是,我在错误报告中使用的示例直接取自标准,它被视为格式正确([12.2p5.4]:

struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };

这与您的情况类似,但是在这里,隐式声明的默认构造函数被定义为已删除,因为该类具有引用类型的非静态成员,该成员没有初始值设定项。

当然,这只是一个例子,但我认为这是一个额外的迹象,表明这些案例没有任何问题。

【讨论】:

  • 我会说它解决了它(我还说它应该得到更多的选票)。
【解决方案2】:

这实际上不是聚合初始化,它是统一的,最近才被 VS 支持。这个警告只是他们没有正确更新它以反映该类型现在可以统一初始化。

聚合可能没有用户定义的非默认非删除构造函数,聚合 UDT 的规则是每个成员也必须是聚合。因此,Baz 不是聚合,直接结果是,Foo 也不是。

【讨论】:

  • 嗯? Foo 是一个聚合(dcl.init.aggr/1),当一个聚合被初始化列表初始化时,它被称为聚合初始化(dcl.init.list/3)。不要求聚合的成员必须是其他聚合。
猜你喜欢
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
相关资源
最近更新 更多