Я начал использовать stl контейнеры, потому что они вошли очень удобные, когда я нуждался в функциональности списка, набора и карты и не имел ничто иное в наличии в моей среде программирования. Я не заботился очень об идеях позади него. Документация STL была интересна до такой степени, когда, она пришла к функциям, и т.д. Затем я пропустил чтение и просто использовал контейнеры.
Но вчера, все еще будучи ослабленным от моего отпуска, я просто дал ему попытку и хотел пойти немного больше stl путем. Таким образом, я использовал функцию преобразования (могу я иметь определенные аплодисменты для меня, спасибо).
С академической точки зрения это действительно выглядело интересным, и это работало. Но вещь, которая беспокоит меня, состоит в том, что при усилении использования тех функций Вам нужны тысячи классов помощника для главным образом всего, что Вы хотите сделать в своем коде. Целая логика программы нарезана в крошечные части. Это разрезание не является результатом хороших привычек кодирования; это - просто техническая потребность. Что-то, которое делает мою жизнь, вероятно, тяжелее не легче.
Я научился на горьком опыте, что необходимо всегда выбирать самый простой подход, который решает проблему под рукой. Я не вижу то, что, например, функция for_each делает для меня, который выравнивает по ширине использование класса помощника по нескольким простым строкам кода, которые находятся в нормальном цикле так, чтобы все видели то, что продолжается.
Я хотел бы знать, что Вы думаете о моих проблемах? Вы видели его как, я делаю, когда Вы начали прокладывать себе путь и передумали, когда Вы привыкли к нему? Есть ли преимущества, которые я пропустил? Или Вы просто игнорируете этот материал, как я сделал (и продолжит делать его, вероятно).
Спасибо.
PS: Я знаю, что существует реальный for_each цикл в повышении. Но я игнорирую его здесь, так как это - просто удобный способ для моих обычных циклов с итераторами, которые я предполагаю.
Вы найдете разногласия среди экспертов, но я бы сказал, что for_each
и transform
немного отвлекают. Сила STL в том, чтобы отделить нетривиальные алгоритмы от данных, с которыми работают.
Определенно стоит поэкспериментировать с лямбда-библиотекой Boost, чтобы увидеть, как вы с ней справитесь. Однако, даже если вы найдете синтаксис удовлетворительным, огромное количество задействованного оборудования имеет недостатки с точки зрения времени компиляции и возможности отладки.
Я советую использовать:
for (Range::const_iterator i = r.begin(), end = r.end(); i != end(); ++i)
{
*out++ = .. // for transform
}
вместо for_each
и преобразовать
, но, что более важно, познакомьтесь с алгоритмами, которые очень полезны : ] sort
, unique
, rotate
, чтобы выбрать три наугад.
Увеличение счетчика для каждого элемента последовательности не является хорошим примером для for_each
.
Если вы посмотрите на лучшие примеры, вы можете обнаружить, что это делает код более понятным для понимания и использования.
Это код, который я написал сегодня:
// assume some SinkFactory class is defined
// and mapItr is an iterator of a std::map<int,std::vector<SinkFactory*> >
std::for_each(mapItr->second.begin(), mapItr->second.end(),
checked_delete<SinkFactory>);
checked_delete
является частью boost, но реализация тривиальна и выглядит так:
template<typename T>
void checked_delete(T* pointer)
{
delete pointer;
}
Альтернативой было бы написать следующее:
for(vector<SinkFactory>::iterator pSinkFactory = mapItr->second.begin();
pSinkFactory != mapItr->second.end(); ++pSinkFactory)
delete (*pSinkFactory);
More than что, как только у вас есть этот checked_delete
, записанный один раз (или если вы уже используете boost), вы можете удалять указатели в любой последовательности где угодно, с тем же кодом, не заботясь о том, какие типы вы повторяете (то есть , вам не нужно объявлять vector
).
Также есть небольшое улучшение производительности из-за того, что с for_each контейнер .end ()
будет вызываться только один раз, и потенциально значительное улучшение производительности зависит от реализации for_each
(это может быть реализовано по-разному в зависимости от полученного тега итератора).
Кроме того, если вы комбинируете boost :: bind с алгоритмами последовательности stl, вы можете делать всевозможные забавные вещи (см. Здесь: http://www.boost.org/doc/libs/1_43_0/libs/bind /bind.html#with_algorithms).
Локальные классы - отличное средство для решения этой проблемы. Например:
void IncreaseVector(std::vector<int>& v)
{
class Increment
{
public:
int operator()(int& i)
{
return ++i;
}
};
std::for_each(v.begin(), v.end(), Increment());
}
IMO, это слишком сложно для простого приращения, и будет проще записать его в форме обычного простого цикла for . Но когда операция, которую вы хотите выполнить над последовательностью, становится более сложной. Затем я считаю полезным четко отделить операцию, выполняемую над каждым элементом, от фактического предложения цикла. Если имя вашего функтора выбрано правильно, код получит описательный плюс.
Это действительно реальные проблемы, и они решаются в следующей версии стандарта C ++ («C ++ 0x»), которая должна быть опубликована либо в конце этого года, либо в 2011 году. Эта версия C ++ вводит понятие под названием Лямбда-выражения C ++ , которые позволяют создавать простые анонимные функции внутри другой функции, что позволяет очень легко выполнять то, что вы хотите, не разбивая код на крошечные кусочки. Лямбды поддерживаются (экспериментально?) В GCC начиная с GCC 4.5.
Я считаю его наиболее полезным при использовании вместе с boost :: bind
и boost :: lambda
, так что мне не нужно писать собственный функтор. Это всего лишь крошечный пример:
class A
{
public:
A() : m_n(0)
{
}
void set(int n)
{
m_n = n;
}
private:
int m_n;
};
int main(){
using namespace boost::lambda;
std::vector<A> a;
a.push_back(A());
a.push_back(A());
std::for_each(a.begin(), a.end(), bind(&A::set, _1, 5));
return 0;
}
Вся логика программы разбита на мелкие кусочки. Такая нарезка не является результатом хороших навыков программирования. Это просто техническая необходимость. Что-то, что делает мою жизнь, наверное, труднее, а не легче.
В некоторой степени вы правы. Вот почему в грядущей редакции стандарта C ++ будут добавлены лямбда-выражения, позволяющие делать что-то вроде этого:
std::for_each(vec.begin(), vec.end(), [&](int& val){val++;})
но я также думаю, что часто бывает хорошей привычкой кодировать разделение кода, как это требуется в настоящее время. Вы эффективно отделяете код, описывающий операцию, которую хотите выполнить, от его применения к последовательности значений. Это лишний шаблонный код, иногда он просто раздражает, но я думаю, что он также часто приводит к хорошему, чистому коду.
Выполнение описанного выше сегодня будет выглядеть так:
int incr(int& val) { return val+1}
// and at the call-site
std::for_each(vec.begin(), vec.end(), incr);
Вместо того, чтобы раздувать сайт вызова полным циклом, у нас есть одна строка, описывающая:
, поэтому он короче и передает ту же информацию, что и цикл, но более кратко.
Я думаю, это хорошие вещи. Недостатком является то, что нам приходится определять функцию incr
где-то еще. А иногда это просто не стоит усилий, поэтому в язык добавляются лямбды.
Думаю, сообщество C ++ обеспокоено теми же проблемами. В новом стандарте C ++ 0x, подлежащем проверке, вводятся лямбда-выражения. Эта новая функция позволит вам использовать алгоритм при написании простых вспомогательных функций непосредственно в списке параметров алгоритма.
std::transform(in.begin(), int.end(), out.begin(), [](int a) { return ++a; })
Такие библиотеки, как STL и Boost , сложны еще и потому, что они должны решать все задачи и работать с любой формой пластины.
Как пользователь этих библиотек - вы не планируете переделывать .NET? - вы можете использовать их упрощенные лакомства.
Вот, возможно, более простой вариант foreach из Boost, который мне нравится использовать:
BOOST_FOREACH(string& item in my_list)
{
...
}
Выглядит намного аккуратнее и проще , чем использование .begin ()
, . end ()
и т. д., но он работает практически для любой итерируемой коллекции (не только для массивов / векторов).