Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

implicit conversion from class to enumeration type in switch conditional

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.

like image 829
Oktalist Avatar asked Aug 05 '14 16:08

Oktalist


2 Answers

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 cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.

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) {
    // ...
like image 110
ecatmur Avatar answered Nov 11 '22 17:11

ecatmur


I believe clang is correct here, depending on which version of the standard is being used. I usually use N3485 as a C++11 after fixes reference but it could be argued that the change that I noted in Classes with both template and non-template conversion operators in the condition of switch statement are an addition and thus are actually part of C++1y.

So going with the contention that contextually implicit conversions is a addition then clang is correct for the draft C++11 standard. Due to section 6.4.2 The switch statement which says (emphasis mine going forward):

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 C++1y then this should be acceptable code and running this in C++1y mode in clang seems to confirm this is indeed the case (see it live).

We can see from the draft C++1y standard section 6.4.2 The switch statement that this involves a contextually implicit conversion. Paragraph 2 says:

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.

We can see the section we need to use is 4 Standard conversions and paragraph 5 covers these cases, it says:

Certain language constructs require conversion to a value having one of a specified set of types appropriate to the construct. An expression e of class type E appearing in such a context is said to be contextually implicitly converted to a specified type T and is well-formed if and only if e can be implicitly converted to a type T that is determined as follows: E is searched for conversion functions whose return type is cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.

like image 39
Shafik Yaghmour Avatar answered Nov 11 '22 17:11

Shafik Yaghmour