Find index of iterator in STL container - need template function - c++

I want to have a function with interface like this:
template<typename T, typename R> int find_index (const T& list, const R& value);
As I know, there is find() in STL that returns iterator. I need to return index of iterator (even for non-indexed containers such as std::list). I tried this code:
template<typename T, typename R>
int find_index (const T& list, const R& value)
{
int index = 0;
for (T::const_iterator it = list.begin(); it != list.end(); it++, index++)
if ((*it) == value)
return index;
return -1;
}
But compiler shows error on it - seems like it is not allowed to get const_iterator from templated typename. Can I go around it?
At the worst case I can pass begin and end iterators to find_index arguments, but it looks not so fine. Would be thankful for elegant solution.

for (typename T::const_iterator it = list.begin(); it != list.end(); ++it, ++index)
should solve your problem.
When using dependent types (types depending on template parameters), the compiler does not know that const_iterator is a type until it instantiates the template with a concrete type, it could also just be a static variable or whatever. Using the typename keyword, you tell him that const_iterator is really a type.
In C++11 you can also circumvent the whole typename issue using the auto keyword:
for (auto it = list.begin(); it != list.end(); ++it, ++index)
If you already have the iterator (maybe from some other operation), you can also just compute the distance from the list's begin to this iterator:
#include <iterator>
int index = std::distance(list.begin(), it);
But since this has linear complexity for a std::list, using your self-made find_index function is a better idea than std::find followed by std::distance, at least performance-wise.

Related

Custom container traversal with range-based for loop

In C++, some STL containers like vector, map, string can be traversed by for loops with colon in it.
for instance:
for(auto c:v)
When I'm writing a custom container, can I make it traversed that way like Java (which only need to implement Iterable)?
Yes, you need to implement some form of iterator and override std::begin(container) and std::end(container) (might work also if you container has begin and end methods).
Internally the code is equivalent to something like the following (this is just to get the point across, the compiler can write it slightly differently, see here for more details).
auto _end = end(v);
for (auto _it = begin(v); _it != _end; ++_it) {
auto c = *_it;
<the rest of loop code>
}
So if your iterator and overrides work as expected it will work for the for loop as well.
You can have a following simple equivalent to Java Iterable interface:
template <typename T, typename U>
struct iterable {
T _begin;
U _end;
iterable(T begin, U end)
: _begin(begin),
_end(end)
{}
T begin() {
return _begin;
}
U end() {
return _end;
}
};
If you wonder why there are T and U when the begin iterator and end iterator should be the same. The reason is that some containers don't have those two iterators of the same type.
Furthermore, you can implement a helper function make_iterable like:
template <typename T, typename U>
iterable<T, U> make_iterable(T t, U u) {
return iterable<T,U>(t, u);
}

Initializing a vector of auto (unknown) type inside a template function in C++

I have a template function inside which I want to generate a vector which is of an unknown type. I tried to make it auto, but compiler says it is not allowed.
The template function gets either iterators or pointers as seen in the test program inside the followed main function. How can the problem be fixed?
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
auto size = distance(beg, end);
vector<auto> temp(size); // <--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(beg, end, temp->begin);
.
.
return ....
}
int main()
{
int bips[] = {3, 7, 0, 60, 17}; // Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
vector<int> v = {10, 5, 4, 14}; // Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}
You cannot use a std::vector of auto. You might use std::iterator_traits instead:
std::vector<typename std::iterator_traits<Iter>::value_type> temp(size);
If you have a C++17-compatible compiler, you may profit from class template argument deduction.
So unless you have a specific reason to fill your vector with std::copy, you could write your code like this:
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
vector temp(beg, end);
// do the remaining stuff
return ....
}
If this feature is not available on your compiler, then I'd vote for
vector<typename iterator_traits<Iter>::value_type> temp(beg, end);
like in Jonathan's answer
You might be looking for something like
std::vector<typename std::remove_reference<decltype(*beg)>::type> temp(beg, end);
Demo
The reason auto doesn't work is because it's not allowed in that context. You may not provide auto in place of a template argument. The correct course of action when you want the compiler to deduce a template argument automatically is to not provide an argument at all. However, in this case, there is no way for the compiler to deduce what that type should be. You must provide the type explicitly.
There are many ways of finding out what the correct type for your vector is. You can use std::iterator_traits to obtain information about an iterator, including the type of value it refers to. You would use typename std::iterator_traits<Iter>::value_type.
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <vector>
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw std::domain_error("empty vector");
auto size = std::distance(beg, end);
using t_value = typename std::iterator_traits<Iter>::value_type;
std::vector<t_value> temp(size);
std::copy(beg, end, temp.begin());
return temp;
}
int main()
{
int bips[] = { 3,7,0,60,17 };//Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
std::vector<int> v = { 10,5,4,14 };//Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}
I would like to point out that there is no reason to check for 0 size ranges. It would correctly return an empty vector.
You can also simplify the body of my_func quite a bit by taking advantage of the fact that std::vector has a constructor that accepts a pair of iterators and copies that range.
template<class Iter>
auto my_func(Iter beg, Iter end)
{
using t_value =typename std::iterator_traits<Iter>::value_type;
return std::vector<t_value>(beg, end);
}
You can extract a pointer/iterator's type information using iterator_traits. value_type is the specific trait that you are interested in, so you can do:
const vector<typename iterator_traits<Iter>::value_type> temp(beg, end);
Live Example
It is not true that the type is not known. The type of the vector you want to create is of the same kind of Iter.
Just get iter Underlying type either using decltype or using iterator type trait as follows:
decltype -> std::vector<typename remove_reference<decltype(*beg)>::type> temp(beg, end);
iterator type trait
as follows
using Type = std::iterator_traits<Iter>::value_type;
std::vector<Type>...
I would solve this slightly differently than your question seems to be asking for.
First, I find ranges to be a better fundamental type than taking two iterators. The two iterators are coupled, they should be one argument. A range is a simple struct of two iterators, with some utility methods:
template<class It>
struct range_t:
std::iterator_traits<It>
{
It b{}, e{};
It begin() const { return b; }
It end() const { return e; }
bool empty() const { return begin()==end(); }
auto size() const { return std::distance(begin(), end()); }
// etc
range_t()=default;
range_t(range_t const&)=default;
range_t(range_t &&)=default;
range_t& operator=(range_t const&)=default;
range_t& operator=(range_t &&)=default;
};
template<class It>
range_t<It> make_range( It s, It f ) { return {std::move(s), std::move(f)}; }
range_ts correctly couple the begin end iterator together.
Now
template<class Range>
auto my_func(Range&& range) {
// todo
}
template<class Iter>
auto my_func(Iter beg, Iter end)
{
return my_func(make_range(std::move(beg), std::move(end)));
}
is the first step. Or just eliminate the two-iterator version entirely, and expect the caller to package up their iterators for you.
template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
// todo
}
Ok, now you want to do this:
auto size = range.size();
vector<auto> temp(size);//<--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(range.begin(), range.end(), temp->begin);
but that is a common operation. So we write it for range:
template<class Range>
auto as_vector( Range const& r ) {
using value_type = typename Range::value_type;
std::vector<value_type> v( range.begin(), range.end() );
return v;
}
giving us:
template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
auto v = as_vector(range);
// ...
return ...;
}
We have decomposed your problem into simple primitives that have meaning, and moved implementation complexity into those primitives. The "business logic" of your my_func no longer cares what steps you take to make a range into a vector.
This makes your my_func more readable, so long as you have some trust that as_vector(range) actually returns that range as a vector.

accessing map with C++98 standard

I have the following C++11 compatible code and I need to compile it with C++98 which doesn't have support for '.at'. How to rewrite it to be compatible with C++98 ?
String suffix("sss");
headers_t& meta = ...;
typedef std::map<std::string, std::string> headerpair_t;
typedef std::map<std::string, headerpair_t> addheader_t;
addheader_t addheader;
for(headerpair_t::const_iterator piter = addheader.at(suffix).begin(); piter != addheader.at(suffix).end(); ++piter){
// Adding header
meta[(*piter).first] = (*piter).second;
}
Just create an at() function which mimicks what C++11 std::map<...>::at() does:
template <typename K, typename V, typename C, typename A>
V const& at(std::map<K, V, C, A> const& m, K const& k) {
typename std::map<K, V, C, A>::const_iterator it(m.find(k));
if (it == m.end()) {
throw std::out_of_range("key not found in map");
}
return it->second;
}
Note that calling at() in each iteration of a loop is a Bad Idea! Searching a std::map<...> is efficient in the theoretical sense but that doesn't mean that it is fast in practice! You are much better off to search the relevant node just one and then keep using it.
You shouldn't use at() in a for loop condition like that. The element does not change between iteration and there is a overhead in retrieving it at every turn. So you should just retrieve it using find and then loop on the iterators:
addheader_t::const_iterator header_iter = addheader.find(suffix); // Retrieve the element
if (header_iter != addheader.end()) // Check that it does exist
{
// Retrieve the sub-map in the pair
const headerpair_t& header_pair_map = it->second;
// Loop on the elements
for (headerpair_t::const_iterator it = header_pair_map.begin(); header_pair_map.end(); ++it)
{
// Use insert to avoid a useless element construction
// Use also `std::make_pair`, but can we directly insert the pair from headerpair ?
meta.insert(std::make_pair(it->first, it->second));
}
}

Declaring a generic iterator

I have the following problem: I need to make a function which takes two iterators and a value and checks if the value is found between the two. The catch: I can only have one template parameter which denotes the type of the elements from the iterators and the value.
My try is like this, but doesn't seem to work:
template <typename T>
T myFind(iterator<std::bidirectional_iterator_tag,T> begin, iterator<std::bidirectional_iterator_tag, T> end, T elem){
// Code
}
But this doesn't work then:
// vector<int> vect; list<string> lst;
myFind(vect.begin(), vect.end(), 15);
myFind(lst.begin(), lst.end(), "some element");
Any ideas?
Code after changes:
template <typename T>
T myFind(T begin, T end,typename std::iterator_traits<T>::value_type elem){
for(T it = begin; it != end; ++it){
if(*it == elem){
return it;
}
}
return end;
}
Can you have one template parameter that is the iterator type? If so:
template <typename It>
typename std::iterator_traits<It>::value_type
myFind(It begin, It end, typename std::iterator_traits<It>::value_type elem){
// ...
}
Otherwise, I think your restriction is too strong.
After your edit: If you want to do - on the iterator that is returned (as you show you do in the comments), you need a random access iterator. However, std::list::iterator is a bidirectional iterator so you can't. You will need to use std::prev (or in C++03, use std::advance).

A reduce function (for many set unions) in C++

What I am trying to do:
I have a simple set union function in C++ using STL, and I'm trying to wrap it in a function that will let me perform the union of arbitrarily many sets contained in STL data structures (e.g. std::list, std::vector, std::forward_list, ...).
How I tried to do it:
To start, my simple set union:
#include <algorithm>
template <typename set_type>
set_type sunion(const set_type & lhs, const set_type & rhs)
{
set_type result;
std::set_union( lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::inserter(result, result.end()) );
return result;
}
where set_type defines some STL std::set<T>, e.g. std::set<int>.
After noticing several times that I end up needing to perform several unions on iterators of sets (in Python this would be a reduce of my sunion function over some iterable object of set_types). For instance, I might have
std::vector<std::set<int> > all_sets;
or
std::list<std::set<int> > all_sets;
etc., and I want to get the union of all sets in all_sets. I am trying to implement a simple reduce for this, which essentially does a (faster, more elegant, non-copying) version of:
sunion(... sunion( sunion( all_sets.begin(), all_sets.begin()+1 ), all_sets.begin()+2 ) , ... )
Essentially, to do this quickly, I just want to declare a set_type result and then iterate through all_sets and insert value in every set in all_sets into the result object:
template <typename set_type>
set_type sunion_over_iterator_range(const std::iterator<std::forward_iterator_tag, set_type> & begin, const std::iterator<std::forward_iterator_tag, set_type> & end)
{
set_type result;
for (std::iterator<std::forward_iterator_tag, set_type> iter = begin; iter != end; iter++)
{
insert_all(result, *iter);
}
return result;
}
where insert_all is defined:
// |= operator; faster than making a copy and performing union
template <typename set_type>
void insert_all(set_type & lhs, const set_type & rhs)
{
for (typename set_type::iterator iter = rhs.begin(); iter != rhs.end(); iter++)
{
lhs.insert(*iter);
}
}
How it didn't work:
Unfortunately, my sunion_over_iterator_range(...) doesn't work with arguments std::vector<set_type>::begin(), std::vector<set_type>::end(), which are of type std::vector<set_type>::iterator. I thought std::vector<T>::iterator returns an iterator<random_access_iterator_tag, T>. A
After compilation failed because of type incompatibility of the iterators, I looked at the stl vector source (located in /usr/include/c++/4.6/bits/stl_vector.h for g++ 4.6 & Ubuntu 11.10), and was surprised to see the typedef for vector<T>::iterator to be typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator;. I had thought that a ForwardIterator was a subtype of RandomAccessIterator, and so should be fine, but clearly I was incorrect, or I would not be here.
How I am grateful and ashamed of inciting your frustration due to my inexperience:
Apologies if I'm showing my ignorance-- I am trying to learn to be a better object oriented programmer (in the past I have simply hacked everything out in C-style code).
I'm doing my best, coach! Please help me out and spare the world from bad code that I would produce without your code ninja insight...
Here's a very naive approach:
std::set<T> result;
std::vector<std::set<T>> all_sets;
for (std::set<T> & s : all_sets)
{
result.insert(std::make_move_iterator(s.begin()),
std::make_move_iterator(s.end()));
}
This invalidates the elements in the source sets, though it doesn't actually move the element nodes over. If you want to leave the source sets intact, just remove the make_move_iterator.
Unfortunately there's no interface for std::set that lets you "splice" two sets in a way that doesn't reallocate the internal tree nodes, so this is more or less as good as you can get.
Here's a variadic template approach:
template <typename RSet> void union(RSet &) { }
template <typename RSet, typename ASet, typename ...Rest>
void union(RSet & result, ASet const & a, Rest const &... r)
{
a.insert(a.begin(), a.end());
union(result, r...);
}
Usage:
std::set<T> result
union(result, s1, s2, s3, s4);
(Similar move-optimizations are feasible here; you can even add some branching that will copy from immutables but move from mutables, or from rvalues only, if you like.)
Here's a version using std::accumulate:
std::set<T> result =
std::accumulate(all_sets.begin(), all_sets.end(), std::set<T>(),
[](std::set<T> & s, std::set<T> const & t)
{ s.insert(t.begin(), t.end()); return s; } );
This version seems to rely on return value optimisation a lot, though, so you might like to compare it to this hacked up and rather ugly version:
std::set<T> result;
std::accumulate(all_sets.begin(), all_sets.end(), 0,
[&result](int, std::set<T> const & t)
{ result.insert(t.begin(), t.end()); return 0; } );
Usually, when using iterators we don't care about the actual category. Just let the implementation sort it out. That means, just change the function to accept any type:
template <typename T>
typename std::iterator_traits<T>::value_type sunion_over_iterator_range(T begin, T end)
{
typename std::iterator_traits<T>::value_type result;
for (T iter = begin; iter != end; ++ iter)
{
insert_all(result, *iter);
}
return result;
}
Note that I have used typename std::iterator_traits<T>::value_type, which is the type of *iter.
BTW, the iterator pattern is not related to OOP. (That doesn't mean it's a bad thing).