Some of the STL containers such as std::list
and std::vector
don't have find()
method as a member function. Why is that? I know that there is the alternative of using std::find
from <algorithm>
but still this use isn't 100% natural.

- 1,743
- 1
- 11
- 27

- 975
- 1
- 10
- 28
-
1Probably because searching an element in sequential containres all has some algorithm, so its made common, playing around with iterators – P0W Sep 02 '14 at 07:06
-
@POW that wouldn't prevent the standard to implement `find()`, would it? I mean it could just be a call to `std::find()`... – Theolodis Sep 02 '14 at 07:08
-
6You might want to read this [article by Steve Myers about encapsulation](http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197) and this [GotW by Herb Sutter about `std::string`](http://www.gotw.ca/gotw/084.htm). Basically, the more of a class's functionality you can implement outside of its direct interface, the better. – Angew is no longer proud of SO Sep 02 '14 at 07:11
-
4@Theolodis But there is no need to do that, or the advantages do not outweigh the disadvantages of bloating the interface. It would have been better if `std::find` could have been specialized to do the right thing for fast-look-up containers. – juanchopanza Sep 02 '14 at 07:12
-
9You've got it backwards. The versions from `
` should be your defaults. There are special member functions only when there are special requirements. – BoBTFish Sep 02 '14 at 07:13 -
@Theolodis Sorry, typo its _same_ not "some" [here](http://stackoverflow.com/questions/25617610/some-containers-in-stl-dont-have-find-function#comment40020915_25617610) – P0W Sep 02 '14 at 07:15
7 Answers
The general design principle is to use std::find
where possible, and implement find
member functions when it is more efficient.
The containers that do have a find
member are containers which have a more efficient element look-up mechanism then the linear search performed in std::find
. For example, binary search trees such as std::set
and std::map
, or hash tables such as their unordered
counterparts.

- 223,364
- 34
- 402
- 480
find
, lower_bound
and upper_bound
member functions are only provided when more efficient than using the non-member equivalents, or when the non-members couldn't operate given the container's public API
Note in particular that std::string
has a find
function which provides std::find()
-like linear search facilities for character searches and std::search()
-like facilities for sub-string searches: while the non-member versions may have the same big-O efficiency, they may well be less efficient given dedicated machine code instructions are often available for "string" searching. There are also historical, convenience, and ease-of-porting factors.
Quite apart from the question of efficiency, it's noteworthy that some containers:
are inherently either sorted (
multi
-set
,map
) or unsorted (unordered_map
,unordered_set
), typically unsorted (e.g.std::string
), or easily either (std::vector
)publicly support forward iteration and/or random access
possibly privately support binary search
have such a specialised public API for element access that potential reuse of the algorithm is relatively limited (e.g.
unordered_map::bucket
/::begin(n)
et al)
It's also of interest that searching in a vector
can be done using a great many algorithms:
std::find
does a brute force linear O(n) search which will "find" lower-index elements first,std::binary_search
requires a sorted vector but jumps around to achieve O(log2n) complexity.other options like extrapolation search and hashing might be applicable
How would you pick which to implement and add as members? Seems a bit arbitrary. Still, the choice of which to use can be important performance-wise: for a million elements, find
averages half-a-million element comparisons before a match and the full million whenever the element's not there, while binary_search
typically takes ~20 comparisons either way.
The containers with find
don't typically provide such flexibility, and the find
and/or lower_bound
/upper_bound
they provide can be seen as replacements for the non-member equivalents, and likely the only reasonable way to search the containers.

- 102,968
- 15
- 177
- 252
Because there's the std::find
function from algorithm
that applies for them.
Generally, containers like std::vector
and std::list
have linear search time complexity. As such attaching to them a member find
function is a redundancy because there's already std::find
. For other containers (e.g., std::set
or std::map
etc.) there's a better way (i.e., faster than linear complexity) to implement searching. As such the implementers implemented these faster searching algorithm as member functions.

- 41,839
- 11
- 94
- 168
-
1
-
1Even if this is technically true, it does not really answer the question. – MatthiasB Sep 02 '14 at 07:05
-
11@MatthiasB he was using the "foot in the door" reputation gathering technique.. post a one-line answer quickly to get some upvotes and then write out the details later. *Now* it answers the question. – M.M Sep 02 '14 at 07:12
-
Since this answer answers the question, would be nice if down-voters explained the reason of their down-vote (if any...). – 101010 Sep 03 '14 at 16:57
Containers which have a search-by-key like feature will have the find method integrated (e.g. map which is internally implemented with a binary tree which can be looked up efficiently).
Others, like the ones you cited, will allow a range search with the std::find but don't have a featured find function because it would have no algorithmic advantage over the std::find (except in sorted/special cases)

- 43,032
- 26
- 132
- 246
-
In my opinion that does not really answer the question. The answer does probably lie in the specifications... – Theolodis Sep 02 '14 at 07:06
-
Using the same function for various containers makes for a clearer API, you don't have to learn the peculiarities of each of the containers, just how to apply one function that you use for all of them.
It's also for code reusability - you use the algorithm that takes iterators from any of the containers that provide them, so the algorithm doesn't have to rely on the container being a std::vector
, std::list
etc.

- 11,026
- 5
- 30
- 49
Such containers as std::vector
, std::list
, std::forward_list
and some others are sequential containers. There is nothing better than sequential search that can be applied to these containers. So there is no need to rewrite the sequential search for each sequential container if it is the same for all these containers.
The exception is class std::basic_string
that initially simulates C-strings that already have special search functions as strchr, strstr and others.

- 301,070
- 26
- 186
- 335
As mentioned in other comments, the design rationale is that vector::find()
can be as efficiently implemented as a non-member function std::find()
. The benefits of using the latter is that it decouples the data-structures and the operators acting on the data-structure, which increases maintainability (this is advantageous for the developers of the library).
However, the benefits of the former is that it would make the API between all containers consistent, and make client code less verbose. This would increase learnability and readability (this is advantageous for the users of the library). Also, consistent API would allow to write generic code. Consider this:
template <typename Container, typename T>
void foo(const Container& c, const T& x) {
if (std::find(c.begin(), c.end(), x) != c.end()) {
// ...
}
}
The above is inefficient when Container
is a std::map
or std::set
. To make it efficient, we'd need to do:
template <typename Container, typename T>
void foo(const Container& c, const T& x) {
if (c.find(x) != c.end()) {
// ...
}
}
But then it doesn't compile for std::vector
and std::list
. This places the burden on users of the library to write their own generic function manually specialized/overloaded for each type they want to support:
template <typename T>
bool contains(const std::vector<T>& c, const T& x) {
return std::find(c.begin(), c.end(), x) != c.end();
}
template <typename T>
bool contains(const std::set<T>& c, const T& x) {
return c.find(x) != c.end();
}
template <typename Container, typename T>
void foo(const Container& c, const T& x) {
if (contains(c, x)) {
// ...
}
}
I acknowledge that making these types of design decisions is hard, but my opinion is that designers of the STL made a mistake here. The very tiny maintainability burden seems quite largely worth the better API and consistency for users. In a nutshell, since find
must be a member function for some containers (for performance), then find
should be a member function for all containers (for consistency). Note that I'm totally okay with other algorithms being non-member functions.
(I mean, come on, a container is by definition something that contains stuff. It should be trivial for users to write a generic and efficient "contains" function. In fact, I'd argue it should be added to the Container concept, but I digress.)

- 7,015
- 4
- 30
- 59