Чтение онлайн

на главную

Жанры

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Майерс Скотт

Шрифт:

Что нам нужно – так это условная конструкция (например, предложение if..else) для типов, которая вычислялась бы во время компиляции. К счастью, в C++ есть необходимые нам средства. Это не что иное, как перегрузка.

Когда вы перегружаете некоторую функцию f, вы указываете параметры разных типов для различных версий. Когда вызывается f, компилятор выбирает наиболее подходящую из перегруженных версий, основываясь на переданных аргументах. Компилятор, по сути, говорит: «Если эта версия лучше всего соответствует переданным параметрам, вызову ее; если лучше подходит другая версия – остановлюсь на ней, и так далее». Видите? Условная конструкция

для типов во время компиляции. Чтобы заставить advance работать нужным нам образом, следует всего лишь создать две версии перегруженной функции, объявив в качестве параметра для каждой из них объекты iterator_category разных типов. Я назову эти функции doAdvance:

template<typename IterT, typename DistT> // использовать эту

void doAdvance(IterT& iter, DistT d, // реализацию для

std::random_access_iterator_tag) // итераторов

{ // с произвольным доступом

iter += d;

}

template<typename IterT, typename DistT> // использовать эту

void doAdvance(IterT& iter, DistT d, // реализацию для

std::bidirectional_iterator_tag) // двунаправленных

{ // итераторов

if(d >= 0) {while(d–) ++iter;}

else {while (d++) –iter;}

}

template<typename IterT, typename DistT> // использовать

void doAdvance(IterT& iter, DistT d, // эту реализацию

std::input_iterator_tag) // для итераторов

{ // ввода

if(d < 0) {

throw std::out_of_range(“Отрицательное направление”); // см. ниже

}

while (d–) ++iter;

}

Поскольку forward_iterator_tag наследует input_iterator_tag, то версия do-Advance для input_iterator_tag будет работать и с однонаправленными итераторами. Это дополнительный аргумент в пользу наследования между разными структурами iterator_tag. Фактически это аргумент в пользу любого открытого наследования: иметь возможность писать код для базового класса, который будет работать также и для производных от него классов.

Спецификация advance допускает как положительные, так и отрицательные значения сдвига для итераторов с произвольным доступом и двунаправленных итераторов, но поведение не определено, если вы попытаетесь сдвинуть на отрицательное расстояние итератор ввода или однонаправленный итератор. Реализации, которые я проверял, просто предполагают, что d – не отрицательно, поэтому входят в очень длинный цикл, пытаясь отсчитать «вниз» до нуля, если им передается отрицательное значение. В коде, приведенном выше, я показал вариант, в котором вместо этого возбуждается исключение. Обе реализации корректны. Это проклятие неопределенного поведения: вы не можете предсказать, что произойдет.

Имея разные перегруженные версии doAdvance, функции advance остается только вызвать их, передав в качестве дополнительного параметра объект, соответствующий типу категории итератора, чтобы компилятор мог применить механизм разрешения перегрузки для вызова правильной реализации:

template <typename IterT, typename DistT>

void advance(IterT& iter, DistT d)

{

doAdvance( // вызвать версию

iter, d, // doAdvance

typename // соответствующую

std::iterator_traits<IterT>::iterator_category // категории

); // итератора iter

}

Подведем итоги – как нужно использовать класс-характеристику:

• Создать набор перегруженных «рабочих» функций либо шаблонов функций (например, doAdvance), которые отличаются параметром-характеристикой. Реализовать каждую функцию в соответствии с переданной характеристикой.

• Создать «ведущую» функцию либо шаблон функции (например, advance), которая вызывает рабочие функции, передавая информацию, предоставленную классом-характеристикой.

Классы-характеристики широко используются в стандартной библиотеке. Так, класс iterator_traits, помимо iterator_category, представляет еще четыре вида информации об итераторах (наиболее часто используется value_type; в правиле 42 показан пример его применения). Есть еще char_traits, который содержит информацию о символьных типах, и numeric_limits, который хранит информацию о числовых типах, например минимальных и максимальных значениях и т. п. Имя numeric_limits немного сбивает с толку, поскольку нарушает соглашение, в соответствии с которыми имена классов-характеристик должны оканчиваться на «traits», но тут уж ничего не поделаешь, придется смириться.

В библиотеке TR1 (см. правило 54) есть целый ряд новых классов-характеристик, которые предоставляют информацию о типах, включая is_fundamental<T> (где T – встроенный тип), is_array<T> (где T – тип массива) и is_base_of<T1,T2> (то есть является ли T1 тем же, что и T2, либо его базовым классом). Всего TR1 добавляет к стандартному C++ более 50 классов-характеристик.

Что следует помнить

• Классы-характеристики делают доступной информацию о типах во время компиляции. Они реализованы с применением шаблонов и их специализаций.

• В сочетании с перегрузкой классы-характеристики позволяют проверять типы во время компиляции.

Правило 48: Изучите метапрограммирование шаблонов

Метапрограммирование шаблонов (template metaprogramming – TMP) – это процесс написания основанных на шаблонах программ на C++, исполняемых во время компиляции. На минуту задумайтесь об этом: шаблонная метапрограмма – это программа, написанная на C++, которая исполняется внутри компилятора C+ +. Когда TMP-программа завершает исполнение, ее результат – фрагменты кода на C++, конкретизированные из шаблонов, – компилируется как обычно.

Поделиться:
Популярные книги

На границе империй. Том 6

INDIGO
6. Фортуна дама переменчивая
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.31
рейтинг книги
На границе империй. Том 6

Курсант: Назад в СССР 4

Дамиров Рафаэль
4. Курсант
Фантастика:
попаданцы
альтернативная история
7.76
рейтинг книги
Курсант: Назад в СССР 4

С Новым Гадом

Юнина Наталья
Любовные романы:
современные любовные романы
эро литература
7.14
рейтинг книги
С Новым Гадом

Наследник

Кулаков Алексей Иванович
1. Рюрикова кровь
Фантастика:
научная фантастика
попаданцы
альтернативная история
8.69
рейтинг книги
Наследник

Приручитель женщин-монстров. Том 6

Дорничев Дмитрий
6. Покемоны? Какие покемоны?
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Приручитель женщин-монстров. Том 6

Убивать чтобы жить 3

Бор Жорж
3. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 3

Убивать, чтобы жить

Бор Жорж
1. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать, чтобы жить

Релокант. Вестник

Ascold Flow
2. Релокант в другой мир
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Релокант. Вестник

Неудержимый. Книга XIX

Боярский Андрей
19. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XIX

Моя (не) на одну ночь. Бесконтрактная любовь

Тоцка Тала
4. Шикарные Аверины
Любовные романы:
современные любовные романы
7.70
рейтинг книги
Моя (не) на одну ночь. Бесконтрактная любовь

Папина дочка

Рам Янка
4. Самбисты
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Папина дочка

Первый пользователь. Книга 3

Сластин Артем
3. Первый пользователь
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Первый пользователь. Книга 3

Назад в СССР: 1985 Книга 2

Гаусс Максим
2. Спасти ЧАЭС
Фантастика:
попаданцы
альтернативная история
6.00
рейтинг книги
Назад в СССР: 1985 Книга 2

В теле пацана 4

Павлов Игорь Васильевич
4. Великое плато Вита
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
В теле пацана 4