Является ли обтекание идиом STL для удобства чтения хорошей идеей?

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

Ответ 1

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

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

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

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

Ответ 2

boost делает его намного опрятным. Я никогда не использую STL итераторов. Диапазон основанные алгоритмы - намного более аккуратная абстракция и приводят к значительно более чистым кодам.

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

void foo(){
    std::vector<int> list;
    ...
    ...
    boost::find(list, 2) != list.end()
}

Ответ 3

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

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

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

Ответ 4

Аналогично, в моем текущем проекте у нас есть файл с именем: 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();
}

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

Ответ 5

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

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

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

Ответ 6

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

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