struct StringSize:
public_unary_function
string::size_type operator() (const string& s) const
{
return s.size();
}
transform (s.begin(),s.end(),
Ostream_iterator
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
average
};
};
Многие компиляторы принимают этот код, но по Стандарту С++ он считается недопустимым. Дело в том, что теоретически может существовать другой шаблон функции с именем average, вызываемый с одним параметром-типом. В этом случае выражение average
становится неоднозначным, поскольку непонятно, какой шаблон в нем упоминается. В конкретном примере неоднозначность отсутствует, но некоторые компиляторы на вполне законном основании все равно отвергают этот код. Решение основано на использовании объекта функции:
template
struct Average:
public binary_function
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
Average
);
}
Новая версия должна приниматься любым компилятором. Более того, вызовы Average::operator() внутри transform допускают подстановку кода, что не относится к экземплярам приведенного выше шаблона average, поскольку average является шаблоном функции, а не
Таким образом, преимущество объектов функций в роли параметров алгоритмов не сводится к простому повышению эффективности. Объекты функций также обладают большей надежностью при компиляции кода. Бесспорно, «настоящие» функции очень важны, но в области эффективного программирования в STL объекты функций часто оказываются полезнее.
Совет 47. Избегайте «нечитаемого» кода
Допустим, имеется вектор vector
vector
v.erase(
remove_if(find_if(v.rbegin(),v.rend(),
bind2nd(greater_equal
v.end(),
bind2nd(less
v.end());
Всего одна команда, и задача решена. Все просто и прямолинейно. Никаких проблем. Правда?