Обработка последовательных возвратов классов перечисления в clang, gcc и icc последовательно - PullRequest
1 голос
/ 30 октября 2019

Я обычно использую 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){

Это поведение прекрасно, потому что

  1. проверяет согласованность во время компиляции между перечислениями и переключателями, что делает их очень полезными инеразлучная пара черт.
  2. Мне не нужно определять искусственный случай default, для которого мне нечем заняться.
  3. Позволяет мне опустить глобальный возврат, для которого мне было бы нечего возвращать (иногда это не простой тип, такой как 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

Ответы [ 2 ]

1 голос
/ 30 октября 2019

Все эти три компилятора имеют расширение __builtin_unreachable(). Вы можете использовать его как для подавления предупреждения (даже если возвращаемое значение имеет проблемы с конструктором), так и для лучшего генерирования кода:

enum class E{e1, e2};


int fun(E e){
    switch(e){
        case E::e1: return 11;
        case E::e2: return 22;
    }
    __builtin_unreachable();

}

https://godbolt.org/z/0VP9af

0 голосов
/ 30 октября 2019

Это не имеет ничего общего с enum или switch и не имеет ничего общего со способностью компилятора проверять правильное выражение return через каждый путь. Некоторые компиляторы в этом лучше других.

Правильный способ - просто добавить valid return в конце функции.

A fun(E e){
  switch(c){
    case E::e1: return A{11};
    ...
  }
  return A{11}; // can't get here, so return anything
}

Редактировать: некоторые компиляторы (например, MSVC) будут жаловаться, если у вас есть возврат с недоступного пути. Просто заключите в скобки возврат #if для компилятора. Или, как я часто делаю, просто определите RETURN (x), который определяется на основе компилятора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...