以下代码被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};
}
GCC 报错
错误:无法将 '
{Good, 100}
' 从 '<brace-enclosed initializer list>
' 转换为 'S
'
哪个是正确的?标准中讨论了这种情况吗?
以下代码被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};
}
GCC 报错
错误:无法将 '
{Good, 100}
' 从 '<brace-enclosed initializer list>
' 转换为 'S
'
哪个是正确的?标准中讨论了这种情况吗?
int
作为其底层类型)到位域时进行一些缩小转换,这在不是直接的列表初始化中是被禁止的,就像在返回值的情况下一样。我之所以这么认为,是因为如果将枚举转换为非作用域枚举(其中允许隐式转换为int
),或者将位域增加到32,使得完美匹配发生,错误就会消失,即使保留初始化为复制列表初始化。 - Jans这应该是格式良好的,所以这是gcc的一个bug。
我们通过[stmt.return]p2来到聚合初始化,其中写道:
… 带有大括号初始化列表的返回语句通过从指定的初始化列表中进行复制列表初始化([dcl.init.list])来初始化将从函数返回的对象或引用。 …
否则,如果T是一个聚合体,则执行聚合初始化([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} ;
}
观察到,在其他情境下,如gcc并没有问题。
S f(E e1, int x) {
S s {e1,100} ;
return s;
}
所以您确实可以使用解决方法。
10:9: warning: 'S::e' is too small to hold all values of 'enum class E'
,而clang即使枚举范围确实不适合也不会发出警告。也许这种特殊的位域处理部分原因是为什么GCC无法执行聚合初始化的原因之一? - Gem Taylor