开关条件中从类到枚举类型的隐式转换



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错误、g++错误、libstdc++错误、标准缺陷还是其他?我做了什么蠢事吗?

在触发这个问题的代码中,Cstd::atomic<E>, std::atomic<T>::operator T重载了cv限定符constconst volatile

两个编译器都接受E e = c;,所以switch语句似乎有些特殊。

这是c++ 11和c++ 14之间的区别;clang在c++ 14模式(-std=c++1y)下正确接受它,在c++ 11模式(-std=c++11)下拒绝它,而gcc在c++ 11模式下接受它是不正确的。

switch语句的行为由n3323号论文改变,该论文在c++ 11标准最终确定后发表。

[支撑。,在c++ 11中:

2 -条件必须是整型、枚举型,或者存在到整型或枚举型的单一非显式转换函数的类类型(12.3)。[…]

在n3936(措辞按n3323):

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

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

5 -[…][E]查找返回类型为cv T或的转换函数引用cv T,使得T被上下文允许。应该有一个这样的T

switch语句中,上下文隐式转换是到整数或枚举类型,因此C必须至少有一个非explicit转换函数到cv整数或枚举类型或引用cv整数或枚举类型,并且其所有转换函数到cv整数或枚举类型或引用cv整数或枚举类型必须具有相同的底层类型。

一个很好的解决方法(如n3323中提到的)是使用一元加号将switch语句的参数强制转换为算术类型:
  switch (+c) {
    // ...

我相信clang在这里是正确的,这取决于使用的是哪个版本的标准。在修复引用之后,我通常使用N3485作为c++ 11,但可以认为,我在switch语句的条件下使用模板和非模板转换操作符的类中注意到的变化是一个添加,因此实际上是c++ 1y的一部分。

因此,按照上下文隐式转换是一个添加的争论,那么clang对于c++ 11标准草案是正确的。由于6.4.2 部分,切换语句表示(强调mine going forward):

条件必须是整型、枚举型或a为其提供单个非显式转换函数的类类型存在整型或枚举类型(12.3)。[…]

在c++ 1y中,这应该是可接受的代码,并且在clang中以c++ 1y模式运行它似乎证实了这确实是情况(查看它的实时)。

我们可以从c++标准草案6.4.2 switch语句中看到,这涉及到上下文隐式转换。段落2说:

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

我们可以看到我们需要使用的部分是4 标准转换和段落5涵盖了这些情况,它说:

某些语言结构要求转换为具有p的值适合该构造的一组指定类型的。一个类类型e的表达式e出现在这样的上下文中称为上下文隐式地将转换为指定类型T和is当且仅当e可以隐式转换为类型T其确定方法如下:在E中搜索转换函数它的返回类型是cv T或者对cv T的引用使得T是允许的根据上下文。应该有一个这样的t

最新更新