Это продолжение моего вопрос со вчерашнего дня. У меня на уме предупреждение Скотта Мейерса о коде, доступном только для записи. Мне в принципе нравится идея использования стандартных алгоритмов для доступа к ключам или значениям std :: map, но требуемый синтаксис немного барочный ИМХО. Допустим, я хочу сбросить все ключи карты в вектор. Учитывая следующие заявления,
typedef std::map<int, int> MyMap;
MyMap m;
std::vector<int> v;
какой код более удобен в обслуживании (т.е. потенциально менее запутан)?
Опция 1:
std::transform(m.begin(),
m.end(),
std::back_inserter(v),
std::tr1::bind(&MyMap::value_type::first, _1));
Вариант 2:
for (MyMap::iterator i = m.begin(); i != m.end(); ++i)
{
v.push_back(i->first);
}
Вариант 1 более стандартен в библиотеке, но я должен мысленно разложить его, чтобы понять, что происходит. Вариант 2 кажется более легким для чтения за счет возможного небольшого штрафа во время выполнения. У меня нет проблем с процессорным временем, поэтому я склоняюсь к варианту 2. Вы, ребята, согласны? Есть ли третий вариант, который мне следует рассмотреть?
P.S. В процессе написания этого вопроса я пришел к выводу, что лучший способ (для моего проекта) прочитать ключи std :: map - это сохранить их в боковом контейнере и перебрать его. Однако вопрос ремонтопригодности все еще стоит.





Я говорю, давай 2)
Чтобы повысить производительность, вы можете вывести m.end() из цикла и зарезервировать место в векторе.
Не могу дождаться C++ 0x и цикла for на основе диапазона; это сделало бы ваш цикл еще лучше.
Ясность всегда лучше ума. Делайте то, что сможете прочитать позже.
Не только вы думаете, что стандартный код немного тупой. Следующий стандарт C++ представит лямбда-функции, чтобы вы могли писать более разборчивый код с помощью стандартных алгоритмов.
Первый такой же читаемый и обслуживаемый, как и второй - если, вы знаете, что делает bind. Я работаю с Boost :: Bind (по сути идентичным std::tr1::bind) достаточно долго, и у меня с ним нет проблем.
Как только TR1 станет частью официального стандарта, вы можете с уверенностью предположить, что любой компетентный программист на C++ поймет его. До тех пор это может вызвать некоторые трудности, но я всегда думаю о долгосрочных перспективах в краткосрочной перспективе.
Вы забыли using namespace std::tr1::placeholders: P
Честно говоря, для таких простых алгоритмов последний код, вероятно, легче поддерживать. Но на самом деле я склоняюсь к первому (особенно когда C++ 1x дает нам лямбды!), Потому что он подчеркивает функциональный стиль программирования, который я лично предпочитаю императивному стилю использования цикла.
На самом деле это разные штрихи; стандартные алгоритмы наиболее полезны, когда они сложны или универсальны, но это не так.
Вот как это будет выглядеть с лямбдами:
std::transform(m.begin(), m.end(), std::back_insterter(v),
[](MyMap::value_type pair){ return pair.first; }
);
На самом деле, есть другой подход, который я бы предпочел, если бы он не многословен:
using std::tr1::bind;
using std::tr1::placeholders::_1;
std::for_each(m.begin(), m.end(),
bind(&std::vector<int>::push_back, v,
bind(&MyMap::value_type::first, _1)
)
);
И с лямбдами (это, вероятно, по большому счету самый аккуратный и наиболее явный из всех вариантов):
std::for_each(m.begin(), m.end(),
[&v](MyMap::value_type pair){v.push_back(pair.first);}
);
Выберите вариант №1, см. Скотт Мейерс, Эффективный STL, пункт № 43, стр. 181.
Но весь смысл этого вопроса в том, что я пытаюсь уравновесить это понятие с пунктом 47 (избегайте кода только для записи).
@Kristo, если бы у вас действительно были близкие, практические знания STL + TR1, то вариант №1 был бы для вас так же удобочитаем, как и вариант №2.
Когда я вчера посмотрел на ваш вопрос, меня заставила дважды посмотреть не привязка (которую я часто использую), а map :: value_type :: first, которую мне не приходилось использовать очень часто. . Хотя я согласен с тем, что «Ясность всегда превосходит ум», прежде чем ясность, требуется знакомство, и вы не собираетесь знакомиться со стилями, которые вы не используете ...
Я бы также сказал, что, хотя вариант 2 более ясен с точки зрения понимания намеченной цели, он легче скрывает ошибку (любая ошибка в варианте 1 с большей вероятностью будет видна во время компиляции).
Я бы выбрал вариант №3:
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
boost::push_back(v, m | boost::adaptors::map_keys);
Это имеет следующие преимущества:
короче
использует именованную функцию для получения ключей
(потенциально) более эффективен (потому что boost::push_back может вызывать reserve() на v)
и не требует резервной пары v.begin(), v.end().
Любой другой путь - чистое безумие.
Я полностью согласен. Многие люди беспокоятся о «читабельности» некоторого идиоматического фрагмента кода и думают, что, возможно, он не читается, но когда вы думаете, какова их целевая аудитория, это люди, которые ничего не знают о языке, который вы пишете.