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

на главную - закладки

Жанры

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

Майерс Скотт

Шрифт:

Widget temp(w);

temp.normalize;

temp.swap(w);

}

}

Вот что мы можем сказать о переменной w в функции doProcessing:

• Поскольку объявлено, что переменная w имеет тип Widget, то w должна поддерживать интерфейс Widget. Мы можем найти точное описание этого интерфейса в исходном коде (например, в заголовочном файле для Widget), поэтому я называю его явным интерфейсом – явно присутствующим в исходном коде программы.

• Поскольку некоторые из функций-членов Widget являются виртуальными, то вызовы этих функций

посредством w являются примером полиморфизма времени исполнения: конкретная функция, которую нужно вызвать, определяется во время исполнения на основании динамического типа w (см. правило 37).

Мир шаблонного и обобщенного программирования принципиально отличается. В этом мире явные интерфейсы и полиморфизм времени исполнения продолжают существовать, но они менее важны. Вместо них на передний план выходят неявные интерфейсы и полиморфизм времени компиляции. Чтобы понять, что это означает, посмотрите, что произойдет, если мы превратим функцию doProcessing в шаблон функции:

template<typename T>

void doProcessing(T& w)

{

if(w.size > 10 && w != someNastyWidget) {

T temp(w);

temp.normalize;

temp.swap(w);

}

}

Что теперь можно сказать о переменной w в шаблоне doProcessing?

• Теперь интерфейс, который должна поддерживать переменная w, определяется операциями, выполняемыми над w в шаблоне. В данном случае видно, что тип переменной w (а именно T) должен поддерживать функции-члены size, normalize и swap; конструктор копирования (для создания temp), а также операцию сравнения на равенство (для сравнения с someNastyWidget). Скоро мы увидим, что это не совсем точно, но на данный момент достаточно. Важно, что набор выражений, которые должны быть корректны для того, чтобы шаблон компилировался, представляет собой неявный интерфейс, который тип T должен поддерживать.

• Для успешного вызова функций, в которых участвует w, таких как operator> и operator!=, может потребоваться конкретизировать шаблон. Такая конкретизация происходит во время компиляции. Поскольку конкретизация шаблонов функций с разными шаблонными параметрами приводит к вызову разных функций, мы называем это полиморфизмом времени компиляции.

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

Явные интерфейсы обычно состоят из сигнатур функций, то есть имен функций, типов параметров, возвращаемого значения и т. д. Так, открытый интерфейс класса Widget

class Widget {

public:

Widget;

virtual ~Widget;

virtual std::size_t size const;

virtual void normalize;

void swap(Widget& other);

};

состоит из конструктора, деструктора и функций size, normalize и swap вместе с типами их параметров, возвращаемых значений и признаков константности (интерфейс также

включает генерируемые компилятором конструктор копирования и оператор присваивания – см. правило 5). В состав интерфейса могут входить также typedefbi.

Неявный интерфейс несколько отличается. Он не базируется на сигнатурах функций. Вместо этого он состоит из корректных выражений. Посмотрим еще раз на условия в начале шаблона doProcessing:

template<typename T>

void doProcessing(T& w)

{

if(w.size > 10 && w != someNastyWidget) {

...

Неявному интерфейсу T (типа переменной w) присущи следующие ограничения:

• Он должен предоставлять функцию-член по имени size, которая возвращает целое значение.

• Он должен поддерживать функцию operator!=, которая сравнивает два объекта типа T. (Здесь мы предполагаем, что someNastyWidget имеет тип T.)

Благодаря возможности перегрузки операторов ни одно из этих требований не должно удовлетворяться в обязательном порядке. Да, T должен поддерживать функцию-член size, хотя стоит упомянуть, что эта функция может быть унаследована от базового класса. Но эта функция не обязана возвращать целочисленный тип. Она даже может вообще не возвращать числовой тип. Вообще-то она даже не обязана возвращать тип, для которого определен operator>! Нужно лишь, чтобы она возвращала объект такого типа X, что может быть вызван operator>, которому передаются параметры типа X и int (потому что 10 имеет тип int). При этом функция operator> может и не принимать параметра, тип которого в точности совпадает с X; достаточно, если тип ее параметра Y может быть неявно преобразован к типу X!

Аналогично не требуется, чтобы тип T поддерживал operator!=, достаточно будет и того, чтобы функция operator!= принимала один объект типа X и один объект типа Y. Если T можно преобразовать в X, а someNastyWidget в Y, то вызов operator!= будет корректным.

(Кстати говоря: мы не принимаем во внимание возможность перегрузки operator&&, в результате которой семантика приведенного выражения может стать уже не конъюнкцией, а чем-то совершенно иным.)

У большинства людей голова идет кругом, когда они начинают задумываться о неявных интерфейсах, но на самом деле ничего страшного в них нет. Неявные интерфейсы – это просто набор корректных выражений. Сами по себе выражения могут показаться сложными, но налагаемые ими ограничения достаточно очевидны.

if(w.size > 10 && w != someNastyWidget)...

Мало что можно сказать об ограничениях, налагаемых функциями size, operator>, operator&& или operator!=, но идентифицировать ограничения всего выражения в целом легко. Условная часть предложения if должна быть булевским выражением, поэтому независимо от конкретных типов результат вычисления (w.size > 10 && w!= someNastyWidget) должен быть совместим с bool. Это та часть неявного интерфейса, которую шаблон doProcessing налагает на свой параметр типа T. Кроме того, для работы doProcessing необходимо, чтобы интерфейс типа T допускал обращения к конструктору копирования, а также функциям normalize, size и swap.

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

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

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

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

Темный Лекарь 4

Токсик Саша
4. Темный Лекарь
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Темный Лекарь 4

Младший сын князя

Ткачев Андрей Сергеевич
1. Аналитик
Фантастика:
фэнтези
городское фэнтези
аниме
5.00
рейтинг книги
Младший сын князя

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

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

Сыночек в награду. Подари мне любовь

Лесневская Вероника
1. Суровые отцы
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Сыночек в награду. Подари мне любовь

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

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

Гром над Империей. Часть 1

Машуков Тимур
5. Гром над миром
Фантастика:
фэнтези
5.20
рейтинг книги
Гром над Империей. Часть 1

Возвышение Меркурия. Книга 3

Кронос Александр
3. Меркурий
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 3

Жандарм 4

Семин Никита
4. Жандарм
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Жандарм 4

Корсар

Русич Антон
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
6.29
рейтинг книги
Корсар

Сломанная кукла

Рам Янка
5. Серьёзные мальчики в форме
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Сломанная кукла

Отмороженный 3.0

Гарцевич Евгений Александрович
3. Отмороженный
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Отмороженный 3.0

Крестоносец

Ланцов Михаил Алексеевич
7. Помещик
Фантастика:
героическая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Крестоносец

Чужое наследие

Кораблев Родион
3. Другая сторона
Фантастика:
боевая фантастика
8.47
рейтинг книги
Чужое наследие