在switch条件语句中将类隐式转换为枚举类型

19

g++ 4.9.0接受以下代码:

enum E { foo };

struct C {
  operator E() const { return foo; }
  operator E() { return foo; }
};

int main() {
  C c;
  switch (c) {
    case foo: break;
  }
}

但是clang 3.4.1会拒绝它,并显示以下诊断信息:

12 : error: multiple conversions from switch condition type 'C' to an integral or enumeration type
switch (c)
^ ~
5 : note: conversion to enumeration type 'E'
operator E() const { return foo; }
^
6 : note: conversion to enumeration type 'E'
operator E() { return foo; }
^

哪一个是正确的?这是clang的bug,g++的bug,libstdc++的bug,标准缺陷还是其他问题?我做了什么愚蠢的事情吗?
在引发这个问题的代码中,C是std::atomic,而std::atomic::operator T被重载为const和const volatile的cv限定符。
两个编译器都接受E e = c;,所以看起来与switch语句有关。

3
这个问题相关。 - Shafik Yaghmour
2
请注意,clang 3.4.1 在使用“-std=c++1y”时是可以接受的。 - ecatmur
正如dyp先前注意到的(该评论现已消失),问题的关键在于上下文隐式转换是否是C ++ 11的一部分,这就是为什么这与在switch语句的条件中具有模板和非模板转换运算符的类相关,尽管特定问题不同。 - Shafik Yaghmour
2个回答

18
这是C++11和C++14之间的一个区别; 在C++14模式下(-std=c++1y),clang正确地接受它,并在C++11模式下(-std=c++11)拒绝它,而gcc在C++11模式下接受它是错误的。
通过论文n3323更改了switch语句的行为,该论文发布后才定稿C++11标准。
在C++11中,[stmt.switch]如下:

2 - 条件应为整数类型、枚举类型或类类型,对于该类类型存在一个非显式转换函数到整数或枚举类型(12.3)。[...]

在n3936中(根据n3323的措辞):

2 - 条件应为整数类型、枚举类型或类类型。如果是类类型,则将条件在上下文中隐含转换(第4条)为整数或枚举类型。

上下文隐式转换是隐式转换的一种变体(即需要声明T t = e才能正常工作);为了使上下文隐式转换有效,允许类类型E具有多个转换函数,但在上下文中有效的所有转换函数必须具有相同的返回类型模数cv和引用限定符:[conv]

5 - [...]搜索E的转换函数,其返回类型为cvT或对cvT的引用,以便上下文允许使用T。 必须恰好有一个这样的T

在一个switch语句中,上下文隐式转换是到整数或枚举类型,所以C必须至少有一个非explicit转换函数到cv整数或枚举类型或引用cv整数或枚举类型,而所有它的转换函数到cv整数或枚举类型或引用cv整数或枚举类型必须具有相同的基础类型。

一个相当不错的解决方法(如n3323中所述)是使用一元加号将switch语句的参数强制转换为算术类型:

  switch (+c) {
    // ...

在C++11模式下失败是因为有多个转换吗? - David G
@0x499602D2 是的,没错;在C++14中是可以的,因为虽然有两个转换,但它们是到相同的类型,除了cv和引用。 - ecatmur

5
我相信在使用哪个版本的标准取决于clang是否正确。我通常使用N3485作为C++11修复后的参考,但可以争论我在Classes with both template and non-template conversion operators in the condition of switch statement中指出的更改是附加内容,因此实际上是C++1y的一部分。
因此,如果认为上下文隐式转换是一种补充,则clang对于草案C++11标准是正确的。由于第6.4.2switch语句,其中写道(从现在开始强调我的话):

条件必须是整数类型、枚举类型或类类型,对于该类类型存在单个非显式转换函数到整数或枚举类型(12.3)。[...]

在C++1y中,这应该是可接受的代码,并且在clang中以C++1y模式运行似乎证实了这一点(查看实际运行效果)。
我们可以从草案C++1y标准6.4.2switch语句中看到,这涉及上下文隐式转换。第二段说:

条件必须是整数类型、枚举类型或类类型。如果是类类型,则将条件在上下文中隐式转换(第4条)为整数或枚举类型。

我们可以看到我们需要使用的部分是4 标准转换,第五段涵盖了这些情况,它说:
特定的语言结构需要将其转换为具有指定集合中一种适当类型的值。在这种情况下出现的类类型 E 的表达式 e 被称为上下文隐式转换到指定的类型 T,并且仅当 e 可以被隐式转换为类型 T 时才是良好的形式。T 的确定如下:在 E 中搜索返回类型为 cv T 或 cv T 的引用的转换函数,使得 T 在上下文中允许使用。必须恰好存在一个这样的 T。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接