struct StringSize:

public_unary_function { // См. совет 40

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

{

return s.size();

}

transform (s.begin(),s.end(),

Ostream_iterator(cout,"\n"),

StringSize();

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

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

template //Вычисление среднего

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

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

return (vail + val2)/2;

};

template

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

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

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

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

{

transform(

begin1,end1,begin2,

ostream_iterator::value_type>(s,"\n"),

average::value_type> // Ошибка?

};

};

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

template

struct Average:

public binary_function{ // См. совет 40

FPType operator()(FPType val1, FPType val2) const

{

return average(val1,val2);

}

};

template

void writeAverages(InputIter1 begin1, InputIter1 end1,

InputIter2 begin2, ostream& s)

{

transform( begin1,end1,begin2,

ostream_iterator::value_type>(s."\n"),

Average::value_type()

);

}

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

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

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

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

vector v; int х,у;

v.erase(

remove_if(find_if(v.rbegin(),v.rend(),

bind2nd(greater_equal().y)).base(),

v.end(),

bind2nd(less(),x)),

v.end());

Всего одна команда, и задача решена. Все просто и прямолинейно. Никаких проблем. Правда?

Перейти на страницу:

Все книги серии Библиотека программиста

Похожие книги