Переключатели С++ Возврат внутри/вне скобок? перерыв?

В C++ вам не нужно ставить фигурные скобки {} внутри регистров в операторах переключения, если только вы не объявляете переменную.

Рассмотрим следующий код:

int Lesson82a() {
    int test = 1;
    switch (test) {
    case 1:
    {
        int i = 1;
        cout << i << "\n";
        cout << "Inside braces" << "\n";
        return 0;
    }
    case 2:
        cout << "2" << "\n";
    case 3:
        cout << "3" << "\n";
    }
    return -1;
}

int Lesson82b() {
    int test = 1;
    switch (test) {
    case 1:
    {
        int i = 1;
        cout << i << "\n";
    }
    cout << "Outside braces" << "\n";
    return 0;
    case 2:
        cout << "2" << "\n";
    case 3:
        cout << "3" << "\n";
    }
    return -1;
}

void Lesson82() {
    int test = Lesson82a();
    cout << test << "\n";
    test = Lesson82b();
    cout << test;
}

При запуске Lesson82() результат будет следующим:

1
Inside braces
0
1
Outside braces
0

Результат тот же, но есть ли разница?

  1. Должен ли я всегда ставить фигурные скобки независимо от того, объявляю ли я переменную или нет?
  2. Если я поставлю фигурные скобки, имеет ли значение, если я помещу весь свой код в скобки? или только код, которому нужны переменные, которые я объявляю, например, в Lesson82b(), где я запускаю некоторый код и оператор возврата после фигурных скобок.
  3. Должен ли я поставить break; после возврата, даже если тогда он недоступен?

Есть ли разница при компиляции?

В строке 122 здесь: https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/begin/LearnWin32/BaseWindow/cpp/main.cpp#L122 я не мог понять почему оператор return находится за пределами фигурных скобок.

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

«...а что такое лучшие практики?...» обычно основано на мнениях, не относящихся к теме. Типичное руководство по стилю кодирования — следовать стилю текущего проекта.

Richard Critten 25.08.2024 16:36

Один вопрос на пост.

user12002570 25.08.2024 16:50

В этом нет никакого «должен». Существуют варианты, и «лучший» вариант зависит от потребностей вашего проекта и (поскольку обычно существует несколько вариантов достижения требуемого результата) вашего мнения о том, что «лучше». А мнения как задницы – они есть у всех, и они не все одинаковы. Голосование за закрытие соответственно.

Peter 25.08.2024 16:53

Более вопиющая проблема — провал между case 2: и case 3:. Решите, действительно ли вы намерены case 2: также выполнить операторы case 3:. Если да, добавьте атрибут [[fallthrough]]; в конце case 2:. В противном случае добавьте break;. Это сделано для того, чтобы читатель понял, что поведение соответствует задуманному, и когда вы правильно включаете предупреждения, компиляторы должны предупреждать об этом, если у вас их нет. И отсутствующий случай default: обычно также вызывает сомнения в том, соответствует ли поведение во всех случаях предполагаемому.

user17732522 25.08.2024 16:55

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

aschepler 25.08.2024 18:00
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
132
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Должен ли я всегда ставить фигурные скобки независимо от того, объявляю ли я переменную или нет?

Брекеты в этом случае не нужны, и если вам так не станет понятнее, то нет.

Если я поставлю фигурные скобки, имеет ли значение, если я помещу весь свой код в скобки? или только код, которому нужны переменные, которые я объявляю, например, в Lesson82b(), где я запускаю некоторый код и оператор возврата после фигурных скобок.

Нет, это не так. В фигурных скобках должен находиться только код, которому необходим доступ к переменной, объявленной в фигурных скобках.

Должен ли я поставить перерыв; после возвращения, даже если он недоступен?

Нет, это совершенно не нужно и, насколько я вижу, никак не улучшает чтение.

Ответ принят как подходящий

В случаях оператора switch фигурные скобки не требуются, точка.

Назначение фигурных скобок — создать локальный лексический контекст — то есть пространство, в котором может быть создана одна или несколько локальных переменных (и, следовательно, задано определенное ограниченное время жизни). В случае предложения case в заявлении switch срок действия должен быть ограничен. Учитывать:

switch (quux)
{
  case 1:
    int n = quux / 2;
    break;
  case 2:
    std::cout << "Hello " << n << "!\n";
    break;
}

Если quux равно 2, то каково ожидаемое время жизни n? Он вообще создан? Когда мы пытаемся напечатать значение n, есть ли вообще n для печати?

Ответы: нет. Целевая метка переключателя находится прямо над кодом, который его создает.

В C (до C23) это также синтаксическая ошибка: целью меток case должно быть утверждение. Но int n ... — это объявление переменной.
Sorry, I misread your language tag as C. But I’ll leave this here since it is worthwhile info for C anyway.


Мы могли бы переместить объявление перед switch. Давайте попробуем:

int n;
switch (quux)
{
  case 1:
    n = quux / 2;
    break;
  case 2:
    std::cout << "Hello " << n << "!\n";
    break;
}

Теперь, когда quux равно 2, мы сразу переходим к коду, который инициализирует n значением. Когда появятся предупреждения компилятора, вы обязательно увидите что-то вроде:

error: 'n' may be used uninitialized

...это УБ .


Эти два примера — плохой код. (Ага, не делай этого.) Они существуют исключительно для того, чтобы продемонстрировать проблему.

Но предположим, что у вас есть такая конструкция:

int n;
switch (quux)
{
  case 1:
    n = quux / 2;
    std::cout << n << "\n";
    break;
  case 2:
    std::cout << "Hello world!\n";
    break;
}

Конечно, жизнь лучше, не так ли?

Может быть. Но уже отмеченные проблемы все еще существуют. Вы не можете объявить n локальным там, где он используется, а в тех случаях, когда он не используется, он не инициализируется. Я не могу сказать, действительно ли это имеет значение, но компилятор все равно должен об этом подумать, так что это так.

Иииии, в любом случае это хорошая идея, чтобы локальные переменные существовали только тогда и в течение настолько короткого времени, насколько это необходимо.


Решение: создайте локальный контекст, используя { фигурные скобки }. Так всегда было в C и, следовательно, в C++. Всегда было совершенно правильно сделать что-то вроде:

int main(void)
{
  int sum = 0;             // x does not exist
  {                        // (new local context)
    int x = 10;            // x exists!
    while (x) sum += x--;  // x exists!
  }                        // (end of scope: x is destroyed)
  printf( "%d\n", sum );   // x does not exist
  return 0;                // x does not exist
}

Мы можем применить эти знания для использования в утверждениях switch.

switch (quux)
{
  case 1:
    {
      int n = quux / 2;
      std::cout << n << "\n";
    }
    break;
  case 2:
    std::cout << "Hello world!\n";
    break;
}

Это совершенно нормально и справедливо. Предложения case содержат только утверждения. n существует только в местном контексте. Все понятно и, что немаловажно, легко рассуждать компилятору (да и нам, людям).


Теперь операторы return и break привязаны к очень специфическому контексту, заключенному в фигурные скобки. return прикрепляется к контексту функции, заключенному в фигурные скобки. break присоединяется к switch (или контексту цикла). (Он не привязан к case!)

Это означает, что положение return или break относительно локального контекста, заключенного в фигурные скобки, не имеет значения. Оба действительны:

switch (quux)                       |  switch (quux)
{                                   |  {
  case 1:                           |    case 1:
  {                                 |      {
    int n = quux / 2;               |        int n = quux / 2;
    std::cout << n << "\n";         |        std::cout << n << "\n";
    break;                          |      }
  }                                 |      break;
  case 2:                           |    case 2:
    std::cout << "Hello world!\n";  |      std::cout << "Hello world!\n";
    break;                          |      break;
}                                   |  }

В обоих этих случаях локальный контекст корректно завершается до того, как break перейдет в конец всего оператора switch.


Список операторов, следующих за меткой case, подобен любому другому списку операторов: они могут содержать локальные контексты, заключенные в фигурные скобки, любым способом, который вы пожелаете.

switch (quux)
{
  case 1:
    {
      std::cout << "inside\n";
    }
    std::cout << "outside\n";
    {
      std::cout << "inside again, lol\n";
    }
    break;
}

Просто... попробуйте написать читаемый код. Это подводит нас к последнему моменту:


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

switch (quux)
{
  case 1:
  {
    int n = quux / 2;
    std::cout << n << "\n";
    break;
  }
  case 2:
  {
    std::cout << "Hello world!\n";
    break;
  }
}

Черт возьми! Мне показалось, что я прочитал C как тег.

Dúthomhas 25.08.2024 17:49

Этот ответ, по-видимому, охватывает то, что уже знает ОП, и не отвечает на вопрос (т. е., учитывая, что фигурные скобки необходимы для создания лексического контекста для определений переменных, следует ли использовать фигурные скобки в других ситуациях).

JaMiT 25.08.2024 18:18

@JaMiT Хм, я собирался сказать, что да (даже в самом первом предложении), но, перечитывая заголовок Q, я думаю, что должен согласиться. Я отредактирую.

Dúthomhas 25.08.2024 18:22

@JaMiT Отредактировано.

Dúthomhas 25.08.2024 18:51

Другие вопросы по теме