📚 Hub Books: Онлайн-чтение книгРазная литератураC++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+
1 2 3 4 5 6 7 8 9 10 ... 121
Перейти на страницу:
class="p1">Получить к ним доступ можно так:

int fraction, remainder;

const bool success {divide_remainder(16, 3, fraction, remainder)};

if (success) {

  std::cout << "16/3 is " << fraction << " with a remainder of "

            << remainder << 'n';

}

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

 

 Помимо того, что аналогичной возможности нет в языке C, возврат сложных структур в качестве выходных параметров долгое время считался медленным, поскольку объект сначала нужно инициализировать в возвращающей функции, а затем скопировать в переменную, которая должна будет содержать возвращаемое значение на вызывающей стороне. Современные компиляторы поддерживают оптимизацию возвращаемых значений (return value optimization, RVO), что позволяет избежать создания промежуточных копий.

Ограничиваем область видимости переменных в выражениях if и switch

Максимальное ограничение области видимости переменных считается хорошим тоном. Иногда, однако, переменная должна получить какое-то значение, а потом нужно его проверить на соответствие тому или иному условию, чтобы продолжить выполнение программы. Для этих целей в С++17 была введена инициализация переменных в выражениях if и switch.

Как это делается

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

□ Выражение if. Допустим, нужно найти символ в таблице символов с помощью метода find контейнера std::map:

if (auto itr (character_map.find(c)); itr != character_map.end()) {

  // *itr корректен. Сделаем с ним что-нибудь.

} else {

  // itr является конечным итератором. Не разыменовываем.

}

// здесь itr недоступен

□ Выражение switch. Так выглядит код получения символа из пользовательского ввода и его одновременная проверка в выражении switch для дальнейшего управления персонажем компьютерной игры:

switch (char c (getchar()); c) {

  case 'a': move_left();  break;

  case 's': move_back(); break;

  case 'w': move_fwd(); break;

  case 'd': move_right(); break;

  case 'q': quit_game(); break;

  case '0'...'9': select_tool('0' - c); break;

  default:

    std::cout << "invalid input: " << c << 'n';

Как это работает

Выражения if и switch с инициализаторами по сути являются синтаксическим сахаром. Два следующих фрагмента кода эквивалентны:

До C++17:

{

auto var (init_value); if (condition) {

    // Ветвь A. К переменной var можно получить доступ

} else {

    // Ветвь B. К переменной var можно получить доступ

}

// К переменной var все еще можно получить доступ

}

Начиная с C++17:

if (auto var (init_value); condition) {

    // Ветвь A. К переменной var можно получить доступ

} else {

    // Ветвь B. К переменной var можно получить доступ

}

// К переменной var больше нельзя получить доступ

То же верно и для выражений switch.

До C++17:

{

  auto var (init_value); switch (var) {

  case 1: ...

  case 2: ...

  ...

  }

  // К переменной var все еще можно получить доступ

}

Начиная с C++17:

switch (auto var (init_value); var) {

case 1: ...

case 2: ...

...

}

// К переменной var больше нельзя получить доступ

Благодаря описанному механизму область видимости переменной остается минимальной. До С++17 этого можно было добиться только с помощью дополнительных фигурных скобок, как показано в соответствующих примерах. Короткие жизненные циклы уменьшают количество переменных в области видимости, что позволяет поддерживать чистоту кода и облегчает рефакторинг.

Дополнительная информация

Еще один интересный вариант — ограниченная область видимости критических секций. Рассмотрим следующий пример:

if (std::lock_guard<std::mutex> lg {my_mutex}; some_condition) {

  // Делаем что-нибудь

}

Сначала создается std::lock_guard. Этот класс принимает мьютекс в качестве аргумента конструктора. Он запирает мьютекс в конструкторе, а затем, когда выходит из области видимости, отпирает его в деструкторе. Таким образом, невозможно забыть отпереть мьютекс. До появления С++17 требовалась дополнительная пара скобок, чтобы определить область, где мьютекс снова откроется.

Не менее интересный пример — это область видимости слабых указателей. Рассмотрим следующий фрагмент кода:

if (auto shared_pointer (weak_pointer.lock()); shared_pointer != nullptr) {

  // Да, общий объект еще существует

} else {

  // К указателю shared_pointer можно получить доступ, но он является нулевым

}

// К shared_pointer больше нельзя получить доступ

Это еще один пример с бесполезной переменной shared_pointer. Она попадает в текущую область видимости, несмотря на то что потенциально является бесполезной за пределами условного блока if или дополнительных скобок!

Выражения if с инициализаторами особенно хороши при работе с устаревшими API, имеющими выходные параметры:

if (DWORD exit_code; GetExitCodeProcess(process_handle, &exit_code)) {

  std::cout << "Exit code of process was: " << exit_code << 'n';

}

// Бесполезная переменная exit_code не попадает за пределы условия if

GetExitCodeProcess — функция API ядра Windows. Она возвращает код для заданного дескриптора процесса, но только в том случае, если данный дескриптор корректен. После того как мы покинем этот условный блок, переменная станет бесполезной, поэтому она не нужна в нашей области видимости.

Возможность инициализировать переменные внутри блоков if, очевидно, очень полезна во многих ситуациях, особенно при работе с устаревшими API, которые используют выходные параметры.

 

 Всегда ограничивайте области видимости с помощью инициализации в выражениях if и switch. Это позволит сделать код более компактным, простым для чтения, а в случае рефакторинга его будет проще перемещать.

Новые правила инициализатора с фигурными скобками

В C++11 появился новый синтаксис инициализатора с фигурными скобками {}. Он предназначен как для агрегатной инициализации, так и для вызова обычного конструктора. К сожалению, когда вы

1 2 3 4 5 6 7 8 9 10 ... 121
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?