枚举位域和聚合初始化

Enum bitfield and aggregate initialization

以下代码被 clang 6.0.0 接受但被 gcc 8.2 拒绝

enum class E {
  Good, Bad,
};

struct S {
  E e : 2;
  int dummy;
};

S f() {
  return {E::Good, 100};
}

Live godbolt example

GCC 抱怨

error: could not convert '{Good, 100}' from '<brace-enclosed initializer list>' to 'S'

哪一个是正确的?标准中哪里谈到了这种情况?

return {E::Good, 100}; 执行 copy list initialization of the return value. The effect of this list initialization is an aggregate initialization.

那么S是聚合吗? aggregate 的描述因您使用的 C++ 版本而异,但在所有情况下 S 应该是一个聚合,因此应该可以编译。 Clang(和 MSVC)具有正确的行为。

虽然修复很简单。将您的 return 语句更改为 return 正确键入的对象:

return S{E::Good, 100};

这应该是格式正确的,所以这是一个 gcc 错误。

我们最终通过 [stmt.return]p2 进行聚合初始化,其中表示:

… A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization ([dcl.init.list]) from the specified initializer list. …

然后 [dcl.init.list]p3.2 说:

Otherwise, if T is an aggregate, aggregate initialization is performed ([dcl.init.aggr]). …

在这一点上,我们可能想知道这是否是一个缩小转换,因此格式错误,但 [dcl.init.list]p7 没有任何条款涵盖这种情况,[dcl.init.list] 中的其他情况也不适用使这个格式错误。

我们可以看到一个类似的例子,它从等式中删除了枚举,但保留了位域显示,gcc 和 clang 都没有给我们一个缩小转换诊断,我们期望是这种情况,尽管涵盖了这个类似的问题通过 [dcl.init.list]p7.4 虽然格式不错误:

struct S2 {
    int e : 2 ;
    int dummy ;
} ;

S2 foo( int x ) {
   return {x, 100} ;
}

see it live in godbolt

据观察,gcc 在其他情况下似乎没有问题,即

S f(E e1, int x) {  
  S s {e1,100} ;
  return s;
}

所以您确实有可用的解决方法。