Силу в C++ во время компиляции ошибка/предупреждение о неявном проваливаются в переключатель


switch операторы могут быть очень полезны, но приводят к общей ошибке, когда программист забыл оператор break:

switch(val) {
    case 0:
        foo();
        break;
    case 1:
        bar();
        // oops
    case 2:
        baz();
        break;
    default:
        roomba();
}

вы не получите предупреждение, очевидно, так как иногда проваливаются явно нужные. Хороший стиль кодирования предлагает комментировать, когда ваше падение преднамеренно, но иногда этого недостаточно.

Я уверен, что ответа на этот вопрос нет, но: есть ли способ в настоящее время (или в перспективе), чтобы иметь возможность спросить компилятор выдаст ошибку (или хотя бы предупреждение!) если ваш case не имеет хотя бы одного из break; или что-то вроде // fallthru? Было бы неплохо иметь возможность оборонительного программирования для использования switch заявления.

4   52   2015-01-15 17:32:04

4 ответа:

Ну лязгом и -Wimplicit-fallthrough о котором я не знал, но нашел с помощью -Weverything. Поэтому для этого кода он дает мне следующее предупреждение (посмотреть его в прямом эфире):

warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough]
case 2:
^
note: insert '[[clang::fallthrough]];' to silence this warning
case 2:
^
[[clang::fallthrough]]; 
note: insert 'break;' to avoid fall-through
case 2:
^
break; 

единственная документация, которую я могу найти для этого флага, находится в Ссылка На Атрибут он говорит:

атрибут clang::fallthrough используется вместе с -Wimplicit-fallthrough аргумент для аннотирования намеренного провала между переключите метки. Он может быть применен только к пустому оператору помещается в точку выполнения между любым оператором и следующим метка переключателя. Он является общим, чтобы отметить этих местах с определенной комментарий, но этот атрибут предназначен для замены комментариев на более строгая аннотация, которая может быть проверена компилятором.

и предоставляет пример того, как отметить явное падение:

case 44:  // warning: unannotated fall-through
g();
[[clang::fallthrough]];
case 55:  // no warning

это использование атрибут пометить явный провал имеет тот недостаток, что он не переносится. Visual Studio генерировать ошибку и gcc генерирует следующее предупреждение:

warning: attributes at the beginning of statement are ignored [-Wattributes]

который является проблемой, если вы хотите использовать -Werror.

я пробовал это с gcc 4.9 и это выглядит как gcc не поддерживает это предупреждение:

ошибка: нераспознанный параметр командной строки '- Wimplicit-fallthrough'

по состоянию на GCC 7,-Wimplicit-fallthrough поддерживается и __attribute__((fallthrough)) может использоваться для подавления предупреждений, когда fallthrough является преднамеренным. GCC действительно распознает комментарии" fallthrough " в некоторых сценариях, но это может быть запутано довольно легко.

я не вижу способа получения такого предупреждения Visual Studio.

Примечание Чендлер Carruth поясняет, что -Weverything не для использования в производстве:

это безумная группа, которая буквально включает каждое предупреждение в Clang. Не используйте это в своем коде. Он предназначен строго для лязга разработчики или для изучения того, какие предупреждения существуют.

но это полезно для выяснения, какие существуют предупреждения.

в C++17 изменения

в C++17 мы получаем атрибут [[fallthough]] покрытые [dcl.АТР.проваливаемся]Р1:

провал атрибута-токена может быть применен к оператору null (9.2); такой оператор является проваливаемся заявление. Выпадающее значение атрибута-токена должно отображаться не более одного раза в каждом списке атрибутов и не содержать attributeargument- пункт должен присутствовать. Заявление проваливаемся может появиться только в рамках включения переключателя заявление (9.4.2). Следующая инструкция, которая будет выполнена после того, как заявление проваливаемся будет оператор labeled, метка которого является меткой case или меткой по умолчанию для того же оператора switch. Программа плохо сформировано, если нет такого утверждения.

...

[ Example:
void f(int n) {
void g(), h(), i();
switch (n) {
  case 1:
  case 2:
    g();
    [[fallthrough]];
  case 3: // warning on fallthrough discouraged
    h();
  case 4: // implementation may warn on fallthrough
    i();
    [[fallthrough]]; // ill-formed
  }
}
—end example ]

посмотреть живой пример с использованием атрибута.

Я всегда пишу break; перед каждым case следующим образом:

switch(val) {
    break; case 0:
        foo();
    break; case 1:
        bar();
    break; case 2:
        baz();
    break; default:
        roomba();
}

таким образом, это гораздо более очевидно для глаз, если break; отсутствует. Начальная break; избыточна, я полагаю, но это помогает быть последовательным.

это обычная switch заявление, я просто использовал пробелы по-другому, удаляя новую строку, которая обычно находится после break; до следующего case.

совет: если вы последовательно помещаете пустую строку между предложениями case, отсутствие "разрыва" становится более заметным для человека, просматривающего код:

switch (val) {
    case 0:
        foo();
        break;

    case 1:
        bar();

    case 2:
        baz();
        break;

    default:
        roomba();
}

Это не так эффективно, когда есть много кода внутри отдельных предложений case, но это, как правило, плохой запах кода сам по себе.

вот ответ на навязчивую ненависть.

первый, switch заявления-это причудливые gotos. Они могут быть объединены с другими потоками управления (лихо, устройство Даффа), но очевидная аналогия здесь-Гото или два. Вот бесполезный пример:

switch (var) {
    CASE1: case 1:
        if (foo) goto END; //same as break
        goto CASE2; //same as fallthrough
    CASE2: case 2:
        break;
    CASE3: case 3:
        goto CASE2; //fall *up*
    CASE4: case 4:
        return; //no break, but also no fallthrough!
    DEFAULT: default:
        continue; //similar, if you're in a loop
}
END:

Я рекомендую этот? Нет. На самом деле, если вы рассматриваете это только для аннотирования провала, то ваша проблема на самом деле что-то другое.

такой код тут сделать очень ясно, что провал может произойти в случае 1, но, как показывают другие биты, это очень мощный метод в целом, который также поддается злоупотреблению. Будьте осторожны, если вы используете его.

забыл break? Ну, тогда вы также иногда забудете любую аннотацию, которую вы выберете. Забывая учитывать провал при изменении оператора switch? Ты плохой программист. При изменении операторов switch(или действительно любой код), вам необходимо сначала понять них.


честно говоря, я очень редко делаю такого рода ошибки (забывая перерыв) - конечно, меньше, чем я сделал другие "общие" ошибки программирования (например, строгое сглаживание). Чтобы быть в безопасности, я в настоящее время делаю (и рекомендую вам сделать) просто написать //fallthrough, так как это по крайней мере проясняет намерения.

кроме этого, это просто реальность, которую программисты должны принять. Вычитайте свой код после его написания и найдите случайную проблему с отладкой. Такова жизнь.