Я обычно использую clang
для разработки кода, используя все разумные предупреждения, которые я могу (-Wall -Wextra [-Wpedantic]
). Одна из приятных особенностей этой настройки заключается в том, что компилятор проверяет согласованность утверждений switch
по отношению к используемому перечислению. Например, в этом коде:
enum class E{e1, e2};
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22; // if I forget this line, clang warns
}
}
clang
будет жаловаться (предупреждать) , если Я опускаю либо e1
, либо e2
, и , если регистр по умолчанию отсутствует.
<source>:4:12: warning: enumeration value 'e2' not handled in switch [-Wswitch]
switch(e){
Это поведение прекрасно, потому что
- проверяет согласованность во время компиляции между перечислениями и переключателями, что делает их очень полезными инеразлучная пара черт.
- Мне не нужно определять искусственный случай
default
, для которого мне нечем заняться. - Позволяет мне опустить глобальный возврат, для которого мне было бы нечего возвращать (иногда это не простой тип, такой как
int
, это может быть тип без конструктора по умолчанию дляпример.
(Обратите внимание, что я использую enum class
, поэтому я предполагаю только допустимые случаи, поскольку недопустимый случай может быть сгенерирован только противным приведением на конце вызывающих.)
Теперь плохие новости: К сожалению, это быстро ломается при переключении на другие компиляторы.В GCC и Intel (icc) приведенный выше код предупреждает (используя те же флаги), что я не возвращаюсь изvoid function.
<source>: In function 'int fun(E)':
<source>:11:1: warning: control reaches end of non-void function [-Wreturn-type]
11 | }
| ^
Compiler returned: 0
Единственное решение, которое я нашел для этой работы, - это регистр default
и возвращение бессмысленного значения.
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22;
default: return {}; // or int{} // needed by GCC and icc
}
}
Это плохо из-запричины, о которых я говорил выше (и даже не доходящие до случая, когда тип возвращаемого значения не имеет конструктора по умолчанию). Но это также плохо, потому что я могу снова забыть один из случаев перечисления, и теперь clang
не будет жаловаться, потому чтоэто случай по умолчанию.
Итак, в итоге я получил этот уродливый код, который работает на этих компиляторах и предупреждает, когда это возможно по правильным причинам.
enum E{e1, e2};
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22;
#ifndef __clang__
default: return {};
#endif
}
}
или
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22;
}
#ifndef __clang__
return {};
#endif
}
Есть ли лучший способ сделать это?
Это пример: https://godbolt.org/z/h5_HAs
В случае класс не по умолчанию классы У меня действительно нет подходящих вариантов:
A fun(E e){
switch(e){
case E::e1: return A{11};
case E::e2: return A{22};
}
#ifndef __clang__
return reinterpret_cast<A const&>(e); // :P, because return A{} would be invalid
#endif
}
https://godbolt.org/z/3WC5v8