Universal STLish contains ()

I'm annoyed that STL containers do not have a contains() method that returns true if the container contains a false element otherwise. So, I sat down and wrote this:

 template <typename C, typename E> inline bool contains(const C& container, const E& element) { return container.find(element) != container.end(); } 

which works well enough for sets and maps, but not vectors. Or lists. How do I proceed? Should I write extra

 template <typename T> inline bool contains(const vector<T>& container, const T& element) { std::find(vector.begin(), vector.end(), item) != vector.end() } 

and more specific code for other containers? Should I instead rely on the non-optimal use of iterators to validate element by element? I would really hate to do this ... maybe I don't notice some relevant STL functions?

+5
source share
2 answers

If you intend to use this function only in STL containers, and if you no longer need to handle the iterator returned by find , then yes, I suggest you write specific code for these containers. This is the most effective thing you can do.

 template<typename ... Args> struct has_find {}; template<typename T> struct has_find<std::vector<T> > { static const bool value=false; }; template<typename T> struct has_find<std::deque<T> > { static const bool value=false; }; template<typename T, size_t I> struct has_find<std::array<T, I> > { static const bool value=false; }; template<typename T, typename U> struct has_find<std::map<T, U> > { static const bool value=true; }; //... and so on for the handful remaining containers template<bool has_find> struct contains_impl { template <typename C, typename E> bool contains(const C& container, E&& element) const { return container.find(std::forward<E>(element)) != container.end(); } }; template<> struct contains_impl<false> { template <typename C, typename E> bool contains(const C& container, E&& element) const { return std::find(container.cbegin(), container.cend(), std::forward<E>(element)) != container.cend(); } }; template <typename C, typename E> bool contains(const C& container, E&& element) { return contains_impl<has_find<C>::value>().contains(container, std::forward<E>(element)); } 

An alternative would be to use metaprogramming and let the compiler determine if the class contains a particular find function, but it can be a bit overkill ... Anyway, if you want to go this route, you can read the recipes in this thread .

+1
source

I think one of the reasons is the lack of returning std::contains a bool because it is too easy for novice programmers to fall into the trap

 if (std::contains(my_container, some_element)) { auto it = std::find(begin(my_container), end(my_container), some_element); // process *it } 

and now you do the required work twice.

Just idiomatic to write

 auto it = std::find(begin(my_container), end(my_container), some_element); if (it != end(my_container)) { // process *it } 

If you insist on using the contains function, you can strive for the best of both worlds by returning std::pair<bool, iterator> or std::optional<iterator> (included in the basic technical specification of the library or already present in Boost), which You can request the following:

 if (opt = std::contains(my_container, some_element)) { // process *opt } 
+7
source

Source: https://habr.com/ru/post/1206715/


All Articles