Я в настоящее время работаю над проектом C++, который должен иметь как можно меньше внешних зависимостей, и таким образом я в значительной степени придерживаюсь STL и Повышения. До сих пор я почти исключительно жил на Спокойной земле когда дело доходит до C++. В целом я склонен использовать C# и Python, когда я могу.
Сегодня я хотел проверить ли a 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
звонить. Я могу затем использовать это способом, которое подобно спокойному примеру.
У меня есть несколько подобных служебных функций в памяти, которые перенесли бы другие идиомы STL ни по какой другой причине, но (воспринятому) увеличению удобочитаемости. То, что я хочу знать..., действительно ли это - плохая идея? Другие люди делают то же? Я пропускаю что-то решающее? Кодом будет OSS однажды, и я не сделал бы чего-то особенного, которое другой C++ devs нашел бы странным.
Нет ничего плохого в написании полезных функций, которые помогут вам и сделают ваш код чище. Другие люди делают то же самое. Библиотека Boost - это самый большой набор таких вспомогательных функций и классов.
Более того, стандарт C++ явно предлагает расширять Стандартную библиотеку (17.3.1.2/1):
Библиотека может быть расширена программой на C++. Каждый пункт, в зависимости от ситуации, описывает требования, которым должны удовлетворять такие расширения. Такие расширения обычно являются одним из следующих:
- Шаблонные аргументы
- Производные классы
- Контейнеры, итераторы и/или алгоритмы, которые соответствуют соглашению об интерфейсе
boost делает это намного аккуратнее. Я никогда не использую STL алгоритмы, основанные на итераторах. Алгоритмы, основанные на диапазоне основанные на алгоритмах диапазона, являются гораздо более аккуратной абстракцией и приводят к гораздо более чистому коду.
#include <boost/range/algorithm/find.hpp>
void foo(){
std::vector<int> list;
...
...
boost::find(list, 2) != list.end()
}
Достаточно похоже, что в моем текущем проекте у нас есть файл с именем: 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();
}
Есть и другие функции, но я думаю, вы поняли
В других ответах отмечалось, что вы можете написать служебные функции, которые будут делать это за вас, и это хорошая идея там, где вам это нужно. Но я подумал, что отмечу важный момент: STL разработан с учетом алгоритмической эффективности. Почти все операции с STL имеют стандартное требование к эффективности big-O.
Если бы вектор
имел член contains ()
, то, безусловно, было бы O (n) для вызова, поскольку вектор является простым непрерывным списком. Поскольку это также удобно, это может побудить программистов использовать его регулярно, даже с большими наборами данных, поощряя разработку приложений с низкой алгоритмической производительностью. В случае contains ()
, если важно проверить, содержит ли контейнер определенный элемент, с дополнительной гарантией уникальности всех элементов, std :: set
будет почти наверняка лучший выбор, с поиском эффективности O (log n) или даже O (1) для std :: unordered_set
.
Итак, мое личное мнение: изучите все контейнеры и функции, которые предлагает STL, и вы обнаружите, что, хотя он краток, он способствует более эффективному стилю программирования. Вы спрашиваете, упускаете ли вы что-то, и я бы сказал, что да - вы хотите более тщательно подумать о контейнере, который вы используете. В наши дни я регулярно использую набор
вместо вектора
.
Я бы сказал, что это определенно хорошая идея. В C ++ STL отсутствует многое из того, что программисты Python / C # ожидают от стандартной библиотеки. Если вы можете сделать свой код более читаемым, применив подход STL из 2-3 строк и превратив его в единую функцию, продолжайте!
Вот еще один пример очень похожей проблемы: я часто хочу преобразовать int
в std :: string
. К моему удивлению, нет четкого способа сделать это с помощью STL. Итак, я написал функцию toStr
, которая запускает 2-3 строки, необходимые для помещения int
в строковый поток
и возврата результирующей строки
].
Edit: Чтобы уточнить, я рекомендую поискать решения для boost
, прежде чем создавать свои собственные. Мой пример был предназначен для демонстрации ограничений STL, но имеет альтернативную интерпретацию: «все, что в STL отсутствует, есть boost
».
Я не большой поклонник оберток, но если они вам помогут, дерзайте. Я думаю, со временем вы обнаружите, что захотите использовать свою служебную функцию с другими контейнерами, помимо std :: vector. Со временем ваша служебная функция станет настолько универсальной, что вы можете напрямую использовать std :: find.
Но вы уверены, что используете правильный контейнер? std :: set имеет метод count (), который по сути эквивалентен contains (). И это O (log (n)), а не O (n).