Правильное использование C++20 `[вероятно]]`/`[[маловероятно]]` в операторах `switch`

C++20 имеет удобные атрибуты [[likely]]/[[unlikely]], которые определяют генерацию кода. Например, вы можете указать ветку, которая, вероятно, будет выбрана:

if (b) [[likely]] { /*...*/ }

Точно так же можно использовать эти атрибуты в операторах switch. . . как-то? В документации предлагается следующий пример (слегка отформатированный):

switch (i) {
    case 1:
        [[fallthrough]];
    [[likely]] case 2:
        return 1;
}

Подразумевается, что оператор [[likely]]/[[unlikely]] предшествует оператору case. Интернет, кажется, почти повсеместно пропагандирует это использование.

Однако рассмотрим следующий аналогичный код (все, что я сделал, это переместил [[likely]] в другой case):

switch (i) {
    [[likely]] case 1:
        [[fallthrough]];
    case 2:
        return 1;
}

Это не скомпилируется на clang! Хотя это может быть связано с ошибкой компилятора с [[fallthrough]], это заставило меня задуматься по стандартам. соответствующий стандарт содержит следующий пример (см. §VII):

реализации рекомендуется оптимизировать для этого случая (например, a со значением 1 в следующем коде):

switch (a) {
    case 1: [[likely]]
        foo();
        break;
    //...
}

То есть атрибут идет после метки case, а не перед ним.

Так . . . что он? На первый взгляд, я бы ожидал, что стандарт будет правильным, но на самом деле это предложение, а не настоящий стандарт AFAICT - с тех пор он мог быть изменен. И я ожидаю, что документация, по крайней мере, будет правильной, по крайней мере, в отношении основного синтаксиса, за исключением того, что она даже не компилируется.


person imallett    schedule 05.03.2021    source источник


Ответы (1)


Оба примера действительны, и в Clang обнаружена ошибка. Соответствующая формулировка из последнего стандартного черновика для C++20:

[dcl.attr.likelihood]

1 Токены-атрибуты likely и unlikely может применяться к меткам или операторам.

Где соответствующие грамматические продукты для операторов и помеченных операторов имеют последовательность спецификаторов атрибутов в соответствующих местах.

[stmt.pre]

statement:
  labeled-statement
  attribute-specifier-seq expression-statement
  attribute-specifier-seq compound-statement
  attribute-specifier-seq selection-statement
  attribute-specifier-seq iteration-statement
  attribute-specifier-seq jump-statement
  declaration-statement
  attribute-specifier-seq try-block

[stmt.label]

labeled-statement:
  attribute-specifier-seq identifier : statement
  attribute-specifier-seq case constant-expression : statement
  attribute-specifier-seq default : statement

In

switch (i) {
    case 1:
        [[fallthrough]];
    [[likely]] case 2:
        return 1;
}

Атрибут применяется к case 2:, тогда как в

switch (a) {
    case 1: [[likely]]
        foo();
        break;
    //...
}

это относится к заявлению foo();.

person StoryTeller - Unslander Monica    schedule 05.03.2021
comment
Хотя краткость хороша, я чувствую, что правила языковой грамматики здесь очень уместны. Кроме того, мне трудно увидеть разницу между [[likely]], применяемым к foo();, и к оператору case, поскольку (за исключением провала или goto) оператор case является единственным способом достижения foo();. Конечно, я вижу, что [[likely]] foo(); работает как изолированная конструкция, но в документе прямо сказано, что это влияет на case . . . - person imallett; 05.03.2021
comment
@imallett - Он сказал, что должен повлиять на дело (однако, не перейти к меткам), но предложенная там нормативная формулировка упоминает только утверждения. Предложение также не касается необходимых изменений грамматики. Стандарт содержит более уточненную формулировку. И вы не видите разницы, потому что ее нет. Нормативная формулировка просто предназначена для обеспечения более гибкого размещения. - person StoryTeller - Unslander Monica; 05.03.2021