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

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

Жанры

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

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

Усадьба леди Анны

Ром Полина
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Усадьба леди Анны

Чужая дочь

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Чужая дочь

Светлая тьма. Советник

Шмаков Алексей Семенович
6. Светлая Тьма
Фантастика:
юмористическое фэнтези
городское фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Светлая тьма. Советник

Двойник Короля

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

Его нежеланная истинная

Кушкина Милена
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Его нежеланная истинная

Последний Паладин. Том 2

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

Измена. Наследник для дракона

Солт Елена
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Измена. Наследник для дракона

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

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

Мастер темных Арканов

Карелин Сергей Витальевич
1. Мастер темных арканов
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Мастер темных Арканов

Адвокат империи

Карелин Сергей Витальевич
1. Адвокат империи
Фантастика:
городское фэнтези
попаданцы
фэнтези
5.75
рейтинг книги
Адвокат империи

Кодекс Охотника. Книга XXI

Винокуров Юрий
21. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XXI

Вечный. Книга II

Рокотов Алексей
2. Вечный
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Вечный. Книга II

Законы Рода. Том 3

Flow Ascold
3. Граф Берестьев
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 3

Наследник

Шимохин Дмитрий
1. Старицкий
Приключения:
исторические приключения
5.00
рейтинг книги
Наследник