Подтвердить что ты не робот

Неявное преобразование из класса в тип перечисления в условном условном выражении

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++, ошибка libstdС++, стандартный дефект или другое? Я сделал что-то глупое?

В коде, вызвавшем этот вопрос, C есть std::atomic<E>, а std::atomic<T>::operator T перегружается на cv-квалификаторах const и const volatile.

Оба компилятора принимают E e = c;, поэтому он кажется чем-то особенным для оператора switch.

4b9b3361

Ответ 1

Это разница между С++ 11 и С++ 14; clang правильно принимает его в режиме С++ 14 (-std=c++1y) и отклоняет его в режиме С++ 11 (-std=c++11), а gcc неверно, чтобы принять его в режиме С++ 11.

Поведение операторов switch было изменено бумагой n3323, которая приземлилась после завершения стандарта С++ 11.

[stmt.switch], в С++ 11:

2 - Условие должно быть целочисленного типа, типа перечисления или типа класса, для которого существует одна неявная функция преобразования для целочисленного или перечисляемого типа (12.3). [...]

В n3936 (формулировка на n3323):

2 - Условие должно быть целым типом, типом перечисления или типом класса. Если тип класса, условие контекстно неявно преобразованный (раздел 4) в интегральный или перечисляемый тип.

Контекстное неявное преобразование является вариантом неявного преобразования (т.е. требуется декларация T t = e); для корректного формирования контекстного неявного преобразования тип класса E допускается иметь несколько функций преобразования, но все допустимые в контексте должны иметь одинаковый тип возврата по модулю cv и ссылочную квалификацию: [conv]

5 - [...] E выполняется поиск функций преобразования, возвращаемым типом которых является cv T или ссылка на cv T такая, что T разрешается контекстом. Там должно быть ровно одно такое T.

В выражении switch контекстное неявное преобразование относится к интегральному или перечисляемому типу, поэтому C должна иметь хотя бы одну функцию преобразования не explicit в cv интегральный или тип перечисления или ссылку на cv интеграл или перечисление type и все его функции преобразования в cv интегральный или тип перечисления или ссылку на cv integer или тип перечисления должны иметь тот же базовый тип.

Довольно приятное обходное решение (как упоминалось в n3323) заключается в использовании унарного плюса для принуждения аргумента оператора switch к арифметическому типу:

  switch (+c) {
    // ...

Ответ 2

Я считаю, что clang здесь верен, в зависимости от того, какая версия стандарта используется. Обычно я использую N3485 как ссылку на С++ 11 после исправления, но можно утверждать, что изменение, которое я заметил в Классы с операторами преобразования шаблонов и без шаблонов в состоянии оператора switch являются добавлением и, следовательно, фактически являются частью С++ 1y.

Итак, соглашаемся с тем, что контекстно-неявные преобразования являются добавлением, тогда clang является правильным для проекта стандарта С++ 11. Из-за раздела 6.4.2 Оператор switch, в котором говорится (выделение моего хода):

Условие должно быть целочисленного типа, типа перечисления или тип класса, для которого одна неявная функция преобразования существует интегральный или перечисляемый тип (12.3). [...]

В С++ 1y тогда это должен быть приемлемый код и запуск этого в режиме С++ 1y в clang, кажется, подтверждает, что это действительно так (видеть его в прямом эфире).

Из черновик С++ 1y раздел 6.4.2 Оператор switch, который включает контекстуально неявное преобразование. В пункте 2 говорится:

Условие должно быть целочисленного типа, типа перечисления или класса тип. Если тип класса, условие контекстуально неявно преобразованный (раздел 4) в интегральный или перечисляемый тип.

Мы можем видеть, что раздел, который нам нужно использовать, это 4 Стандартные преобразования и параграф 5 охватывают эти случаи, он говорит:

Некоторые языковые конструкции требуют преобразования в значение, имеющее один определенного набора типов, соответствующих конструкции. выражение e типа класса E, появляющееся в таком контексте, называется контекстно-неявно преобразованный в заданный тип T и хорошо сформированный тогда и только тогда, когда e может быть неявно преобразован в тип T который определяется следующим образом: E выполняется поиск функций преобразования чей тип возврата является cv T или ссылкой на cv T, так что T разрешено в контексте. Там должно быть ровно одно такое T.