g++ 4.9.0 accepts the following code:
enum E { foo };
struct C {
operator E() const { return foo; }
operator E() { return foo; }
};
int main() {
C c;
switch (c) {
case foo: break;
}
}
But clang 3.4.1 rejects it with the following diagnostic:
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; }
^
Which one is correct? Is it a clang bug, g++ bug, libstdc++ bug, standard defect, or other? Did I do something stupid?
In the code which triggered this question, C
is std::atomic<E>
, and std::atomic<T>::operator T
is overloaded on the cv-qualifiers const
and const volatile
.
Both compilers accept E e = c;
, so it seems to be something peculiar to the switch
statement.
This is a difference between C++11 and C++14; clang correctly accepts it in C++14 mode (-std=c++1y
) and rejects it in C++11 mode (-std=c++11
), while gcc is incorrect to accept it in C++11 mode.
The behavior of switch
statements was changed by paper n3323, which landed after the C++11 standard was finalized.
[stmt.switch], in C++11:
2 - The condition shall be of integral type, enumeration type, or of a class type for which a single non-explicit conversion function to integral or enumeration type exists (12.3). [...]
In n3936 (wording per n3323):
2 - The condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (Clause 4) to an integral or enumeration type.
Contextual implicit conversion is a variant of implicit conversion (i.e. the declaration T t = e
is required to be well-formed); for contextual implicit conversion to be well-formed the class type E
is allowed to have multiple conversion functions, but all those valid in the context must have the same return type modulo cv and reference qualification: [conv]
5 - [...]
E
is searched for conversion functions whose return type is cvT
or reference to cvT
such thatT
is allowed by the context. There shall be exactly one suchT
.
In a switch
statement, contextual implicit conversion is to an integral or enumeration type, so C
must have at least one non-explicit
conversion function to cv integral or enumeration type or reference to cv integral or enumeration type, and all its conversion functions to cv integral or enumeration type or reference to cv integral or enumeration type must have that same underlying type.
A pretty nice workaround (as mentioned in n3323) is to use unary plus to coerce the argument of the switch
statement to arithmetic type:
switch (+c) {
// ...
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments