Является ли обтекание идиом 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).