Является ли упаковка STL идиом для удобства чтения хорошей идеей? - PullRequest
11 голосов
/ 29 июля 2010

В настоящее время я работаю над проектом C ++, который должен иметь как можно меньше внешних зависимостей, и поэтому я в основном придерживаюсь STL и Boost. До сих пор я почти исключительно жил в Qt-land, когда речь заходит о C ++. В общем, я склонен использовать C # и Python, когда могу.

Сегодня я хотел проверить, содержит ли std::vector определенный предмет. С Qt я бы сделал это так:

QList< int > list;
list.append( 1 );
list.append( 2 );
list.append( 3 );

if ( list.contains( 2 ) )
{
    // do something
}

Красиво и читабельно. Но у std::vector нет метода contains, что было неожиданностью. Хорошо ... какой будет STL идиома для чего-то подобного? Обыскивая, кажется, что это:

std::vector< int > list;
list.push_back( 1 );
list.push_back( 2 );
list.push_back( 3 );

std::vector< int >::const_iterator result =
    std::find( list.begin(), list.end(), 2 );

if ( result != list.end() )
{
    // do something
}

Это (для меня) трудно читаемо и слишком многословно. Поэтому я обнаружил, что пишу служебную функцию, которая берет вектор и значение и возвращает bool в зависимости от того, было ли найдено значение или нет. В основном, шаблонный contains() метод; обертка для вышеупомянутого std::find вызова. Затем я могу использовать это способом, подобным примеру Qt.

Я имею в виду несколько подобных утилитарных функций, которые обернули бы другие идиомы STL ни по какой другой причине, кроме (воспринимаемого) повышения читабельности. Что я хочу знать ... это плохая идея? Другие люди делают то же самое? Я что-то упустил? В какой-то момент код будет OSS, и я бы предпочел не делать ничего особенного, что другие разработчики C ++ сочли бы странным.

Ответы [ 6 ]

9 голосов
/ 29 июля 2010

повышение делает его намного аккуратнее.Я никогда больше не использую алгоритмы на основе итераторов STL.Алгоритмы, основанные на диапазоне, являются намного более чистой абстракцией и приводят к намного более чистому коду.

#include <boost/range/algorithm/find.hpp>

void foo(){
    std::vector<int> list;
    ...
    ...
    boost::find(list, 2) != list.end()
}
6 голосов
/ 29 июля 2010

Нет ничего плохого в написании утилит, которые помогут вам и сделают ваш код чище. Другие люди делают то же самое. Boost library - самый большой набор таких служебных функций и классов.

Можно сказать, что C ++ Standard явно предлагает расширить Стандартную библиотеку (17.3.1.2/1):

Библиотека может быть расширена с помощью программы на C ++. Каждый пункт, в зависимости от обстоятельств, описывает требования, которым должны соответствовать такие расширения. К таким расширениям обычно относятся следующие:

  • Аргументы шаблона
  • Производные классы
  • Контейнеры, итераторы и / или алгоритмы, которые соответствуют интерфейсу
3 голосов
/ 29 июля 2010

Я бы сказал, что это определенно хорошая идея.В C ++ STL отсутствует многое из того, чего программисты Python / C # ожидают от стандартной библиотеки.Если вы можете сделать свой код более читабельным, используя подход из двух строк в STL и сделав его единственной функцией, продолжайте!

Вот еще один пример очень похожей проблемы: я часто хочу преобразовать int до std::string.К моему удивлению, нет никакого краткого способа сделать это, используя STL.Итак, я написал toStr функцию, которая запускает 2-3 строки, необходимые для помещения int в stringstream и возврата полученного string.

Редактировать: чтобы уточнить, я рекомендую посмотретьдля boost решений, прежде чем создавать свои собственные.Мой пример был нацелен на то, чтобы продемонстрировать ограничения STL, но он имеет альтернативную интерпретацию: «что бы STL отсутствовало, boost имеет».

1 голос
/ 04 августа 2010

Я не большой поклонник фантиков, но если они вам помогут, дерзайте. Я думаю, что со временем вы обнаружите, что хотите использовать свою служебную функцию с другими контейнерами, кроме std :: vector. В конечном итоге ваша служебная функция становится настолько общей, что вы также можете использовать std :: find напрямую.

Но вы уверены, что используете правильный контейнер? std :: set имеет метод count (), который по сути эквивалентен contains (). И это O (log (n)), а не O (n).

1 голос
/ 30 июля 2010

В других ответах указывалось, что вы можете написать вспомогательные функции, чтобы сделать это для вас, и это хорошая идея, когда вам это нужно. Но я думал, что укажу на важный момент: STL разработан с учетом алгоритмической эффективности. Почти все операции с STL соответствуют стандартным требованиям эффективности big-O.

Если бы vector имел contains() член, он, безусловно, был бы O (n) для вызова, так как vector - простой непрерывный список. Поскольку это также удобно, это может побудить программистов использовать его регулярно, даже в больших наборах данных, поощряя разработку приложений с низкой алгоритмической производительностью. В случае contains(), если важно проверить, содержит ли контейнер определенный элемент с дополнительной гарантией, что все элементы уникальны, std::set почти наверняка является лучшим выбором с эффективностью O (log n). поиск или даже O (1) для std::unordered_set.

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

1 голос
/ 30 июля 2010

Достаточно похоже, в моем текущем проекте у нас есть файл: stlutils.h, который содержит некоторые методы, такие как contains (). Реализовано как:

template<class Container, class T>
bool contains(const Container& c, const T& value) {
   return std::find(c.begin(), c.end(), value) != c.end();
}

Есть еще функции, но я думаю, вы поняли

...