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

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

Жанры

Эффективное использование 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>
. Из этого вектора требуется удалить все элементы, значение которых меньше х, но оставить элементы, предшествующие последнему вхождению значения, не меньшего у. В голову мгновенно приходит следующее решение:

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

Драконий подарок

Суббота Светлана
1. Королевская академия Драко
Любовные романы:
любовно-фантастические романы
7.30
рейтинг книги
Драконий подарок

На границе империй. Том 10. Часть 3

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 3

Сердце для стража

Каменистый Артем
5. Девятый
Фантастика:
фэнтези
боевая фантастика
9.20
рейтинг книги
Сердце для стража

Жандарм 3

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

Жена на пробу, или Хозяйка проклятого замка

Васина Илана
Фантастика:
попаданцы
фэнтези
5.00
рейтинг книги
Жена на пробу, или Хозяйка проклятого замка

Эволюционер из трущоб. Том 5

Панарин Антон
5. Эволюционер из трущоб
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Эволюционер из трущоб. Том 5

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

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

Неучтенный. Дилогия

Муравьёв Константин Николаевич
Неучтенный
Фантастика:
боевая фантастика
попаданцы
7.98
рейтинг книги
Неучтенный. Дилогия

Бастард Императора. Том 7

Орлов Андрей Юрьевич
7. Бастард Императора
Фантастика:
городское фэнтези
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бастард Императора. Том 7

Бастард Императора. Том 4

Орлов Андрей Юрьевич
4. Бастард Императора
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Бастард Императора. Том 4

Кодекс Крови. Книга VII

Борзых М.
7. РОС: Кодекс Крови
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга VII

Идеальный мир для Лекаря 5

Сапфир Олег
5. Лекарь
Фантастика:
фэнтези
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 5

Камень. Книга 3

Минин Станислав
3. Камень
Фантастика:
фэнтези
боевая фантастика
8.58
рейтинг книги
Камень. Книга 3

Матабар

Клеванский Кирилл Сергеевич
1. Матабар
Фантастика:
фэнтези
5.00
рейтинг книги
Матабар