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

на главную

Жанры

Эффективное использование STL
Шрифт:

Подавление подстановки кода функций объясняет один факт, который кажется невероятным многим опытным программистам C: функция C++

sort
почти всегда превосходит по скорости функцию C
qsort
. Конечно, в C++ приходится создавать экземпляры шаблонов функций и вызывать
operator
, тогда как в C все ограничивается простым вызовом функции, однако все «излишества» C++ теряются во время компиляции. На стадии выполнения
sort
обращается к подставленной функции сравнения (при условии, что функция была объявлена с ключевым словом
inline
, а ее тело доступно на стадии компиляции), тогда как
qsort
вызывает функцию сравнения через указатель. Результат —
sort
работает гораздо быстрее. В моих тестах с вектором, содержащим миллион чисел
double
, превосходство по скорости достигало 670%, но я не призываю верить мне на слово. Вы легко убедитесь в том, что при передаче объектов функций в качестве параметров алгоритмов «плата за абстракцию» превращается в «премию за абстракцию».

Существует и другая причина для передачи объектов функций в параметрах алгоритмов, не имеющая ничего общего с эффективностью. Речь идет о компилируемости программ. По каким-то загадочным причинам некоторые платформы STL отвергают абсолютно нормальный код — это связано с недоработками то ли компилятора, то ли библиотеки, то ли и того и другого. Например, одна распространенная платформа STL отвергает следующий (вполне допустимый) фрагмент, выводящий в

cout
длину всех строк в множестве:

set<string> s;

transform(s.begin, s.end,

 ostream_iterator<string::size_type>(cout, "\n"),

 mem_fun_ref(&string::size)

);

Проблема возникает из-за ошибки в работе с константными функциями классов (такими как

string::size
) в этой конкретной платформе STL. Обходное решение заключается в использовании объекта функции:

struct StringSize:

 public_unary_function<string, string::size_type> { // См. совет 40

 string::size_type operator(const string& s) const {

return s.size;

 }

};

transform (s.begin, s.end,

 ostream_iterator<string::size_type>(cout, "\n"), StringSize;

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

string::size
, что почти наверняка невозможно в предыдущем фрагменте с передачей
mem_fun_ref(&string::size)
. Иначе говоря, определение класса функтора
StringSize
не только обходит недоработки компилятора, но и может улучшить быстродействие программы.

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

template<typename FPType> // Вычисление среднего

FPType average(FPType val1, FPType val2) // арифметического двух

{ //вещественных чисел

 return (val1 + val2)/2;

};

template<typename InputIter1, typename InputIter2>

void writeAverages(InputIter begin1, //
Вычислить попарные

 InputIter end1, // средние значения

 InputIter begin2, // двух серий элементов

 ostream& s) { // в потоке

 transform(begin1, end1, begin2,

ostream_iterator<typename iterator_traits<InputIter1>::value_type>(s, "\n"),

average<typename iterator traits<InputIter1>::value_type>); // Ошибка?

}

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

average
, вызываемый с одним параметром-типом. В этом случае выражение
average<typename iterator_traits<InputIter1>::value_type>
становится неоднозначным, поскольку непонятно, какой шаблон в нем упоминается. В конкретном примере неоднозначность отсутствует, но некоторые компиляторы на вполне законном основании все равно отвергают этот код. Решение основано на использовании объекта функции:

template<typename FPType>

struct Average:

 public binary_function<FPType, FPType, FPType> { // См. совет 40

 FPType operator(FPType val1, FPType val2) const {

return average(val1, val2);

 }

};

template<typename InputIter1, typename InputIter2>

void writeAverages(InputIter1 begin1, InputIter1 end1,

 InputIter2 begin2, ostream& s) {

 transform(begin1, end1, begin2,

 ostream_iterator<typename iterator_traits<InputIter1>::value_type>(s, "\n"),

Average<typename iterator_traits<InputIter1>::value_type);

}

Новая версия должна приниматься любым компилятором. Более того, вызовы

Average::operator
внутри
transform
допускают подстановку кода, что не относится к экземплярам приведенного выше шаблона
average
, поскольку
average
является шаблоном функции, а не объекта функции.

Таким образом, преимущество объектов функций в роли параметров алгоритмов не сводится к простому повышению эффективности. Объекты функций также обладают большей надежностью при компиляции кода. Бесспорно, «настоящие» функции очень важны, но в области эффективного программирования в STL объекты функций часто оказываются полезнее.

Совет 47. Избегайте «нечитаемого» кода

Допустим, имеется вектор

vector<int>
. Из этого вектора требуется удалить все элементы, значение которых меньше х, но оставить элементы, предшествующие последнему вхождению значения, не меньшего у. В голову мгновенно приходит следующее решение:

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

Право налево

Зика Натаэль
Любовные романы:
современные любовные романы
8.38
рейтинг книги
Право налево

Третий. Том 2

INDIGO
2. Отпуск
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
Третий. Том 2

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

Машуков Тимур
6. Гром над миром
Фантастика:
фэнтези
попаданцы
5.25
рейтинг книги
Гром над Империей. Часть 2

Сама себе хозяйка

Красовская Марианна
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Сама себе хозяйка

Бальмануг. Невеста

Лашина Полина
5. Мир Десяти
Фантастика:
юмористическое фэнтези
5.00
рейтинг книги
Бальмануг. Невеста

Аромат невинности

Вудворт Франциска
Любовные романы:
любовно-фантастические романы
эро литература
9.23
рейтинг книги
Аромат невинности

Чемпион

Демиров Леонид
3. Мания крафта
Фантастика:
фэнтези
рпг
5.38
рейтинг книги
Чемпион

Эксперимент

Юнина Наталья
Любовные романы:
современные любовные романы
4.00
рейтинг книги
Эксперимент

Небо для Беса

Рам Янка
3. Самбисты
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Небо для Беса

Идеальный мир для Социопата 7

Сапфир Олег
7. Социопат
Фантастика:
боевая фантастика
6.22
рейтинг книги
Идеальный мир для Социопата 7

Я не князь. Книга XIII

Дрейк Сириус
13. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я не князь. Книга XIII

Камень. Книга пятая

Минин Станислав
5. Камень
Фантастика:
боевая фантастика
6.43
рейтинг книги
Камень. Книга пятая

Романов. Том 1 и Том 2

Кощеев Владимир
1. Романов
Фантастика:
фэнтези
попаданцы
альтернативная история
5.25
рейтинг книги
Романов. Том 1 и Том 2

Ваше Сиятельство 4т

Моури Эрли
4. Ваше Сиятельство
Любовные романы:
эро литература
5.00
рейтинг книги
Ваше Сиятельство 4т