Pass iterator as a function parameter - c++

I try to write a function, which will sum the elements of a container. This container can be Vector, List, Queue, etc... That's why I tried templates.
Unfortunately I get this error:
'C' is not a template
Source:
#include <iostream>
#include <vector>
using namespace std;
template<class C, typename T>
T sum( C<T>::iterator begin, C<T>::iterator end ) {
T s = null;
for (C<T>::iterator it = begin; it != end; it++) {
s += *it;
}
return s;
}
int main()
{
vector<int> v = {5, 9, 0, 11};
cout << sum(v.begin(), v.end()) << endl;
return 0;
}
What do I wrong? How should I fix it?

You could express the whole thing in terms of a iterator type, and use iterator_traits to get the value_type:
#include <iterator>
template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
sum(Iterator begin, Iterator end)
{
using value_type = typename std::iterator_traits<Iterator>::value_type;
value_type s = value_type();
for (Iterator it = begin; it != end; it++) {
s += *it;
}
return s;
}
In real life, use std::accumulate:
int sum = std::accumulate(v.begin(), v.end(), 0);

The particular error you get is because you'd need a template template argument:
template<template <typename> class C, typename T>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
T sum( C<T>::iterator begin, C<T>::iterator end )
However, the standard containers typically have more than just one template argument:
template < class T, class Alloc = allocator<T> > class vector
and it is a bit non-trivial to write such function correctly. You could use variadic template arguments, or you could do like the standard library does, and only specialize as much as you really need:
// <algorithm>
namespace std {
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
}
In your case (pretending that your need is not covered by the standard algorithms library already):
template <typename Iterator>
auto sum(Iterator begin, Iterator end)
-> decltype(*begin+*begin) // the type of summing two of them
{
if (begin == end) throw std::logic_error("....");
auto s = *begin;
++begin;
for (; begin != end; ++begin) {
s += *begin;
}
return s;
}
There are some more differences from your original code:
the new code does not assume a null or a default constructor defined (T s = null;)
does not introduce additional iterator (it)
uses pre-increment
throws an exception when begin==end
If you add an init parameter, you can make it almost noexcept:
template <typename Iterator, typename T>
T sum(Iterator begin, Iterator end, T init)
{
for (; begin!=end; ++begin)
init += *begin;
return init;
}
But only almost, because init += *begin could still throw.
If you have such signature, you've by the way reproduced the signature of std::accumulate.

Related

Template function with template argument parametrized over int

Without using the features of C++11 and higher (I will accept them but would prefer C++98),
I have to write a template function with argument T which is an STL container of ints. It receives such a container along with another int which it tries to search for,
Right now I have this but it doesn't compile:
template <template<int> class T>
T::iterator easyfind(T &container, int val)
{
T::iterator it = container.begin();
for ( ; it != container.end(); it++)
if (val == *it)
break ;
return (it);
}
I wonder if I can somehow force the T parameter to always be a class template that is parametrized over integers... I tried writing T<int> but it still doesn't compile.
[NOTE] This answer uses C++20. #PatrickRoberts made me notice that you were preferably requesting a C++98 solution. I leave it anyway because it may be of any help to you.
You can just add a requirement for your template, checking the container's type is int.
[Demo]
#include <iostream> // cout
#include <list>
#include <type_traits> // is_same
#include <vector>
template <typename C>
requires std::is_same<typename C::value_type, int>::value
auto easyfind(const C& container, int val)
{
for (auto it{std::cbegin(container)}; it != std::cend(container); ++it)
{
if (val == *it) { return it; }
}
return std::cend(container);
}
int main()
{
std::vector<int> vi{1, 2, 3};
if (auto it{easyfind(vi, 2)}; it != std::cend(vi))
{
std::cout << *it << "\n";
}
std::list<int> li{4, 5, 6};
if (auto it{easyfind(li, 8)}; it != std::cend(li))
{
std::cout << *it << "\n";
}
std::vector<double> vd{0.5, 1.3, 2.8};
//if (auto it{easyfind(vd, 1.3)}; it != std::cend(vd)) // error
//{
// std::cout << *it << "\n";
//}
}
Though there is an accepted answer, let me try to solve it using C++98.
DEMO
#include <vector>
#include <iostream>
namespace details{
struct true_type{static const bool value = true;};
struct false_type{static const bool value = false;};
template<typename T1,typename T2> struct is_same : false_type{};
template<typename T> struct is_same<T,T>:true_type{};
#define STATIC_ASSERT(expr, msg) \
{ \
char STATIC_ASSERT##msg[(expr)?1:-1]; \
}
};
template <class T>
typename T::iterator easyfind(T &container, int val)
{
using namespace details;
//static_assert can be used in C++11 onwards
STATIC_ASSERT((is_same<typename T::value_type,int>::value == true_type::value),InavalidType);
typename T::iterator it = container.begin();
for ( ; it != container.end(); it++)
if (val == *it)
break ;
return (it);
}
int main(){
std::vector<int> a{1,2,3};
auto it = easyfind(a,1);
if(it != a.end())
std::cout<<*it<<std::endl;
auto it2 = easyfind(a,4);
if(it2 != a.end())
std::cout<<*it<<std::endl;
else
std::cout<<"Not Found"<<std::endl;
std::vector<double> b{1.0,2.0,3.0};
// std::vector<int>::iterator it3 = easyfind(b,1.0); //error
return 0;
}
template <template<int> class T> is not what you expect.
You want
template <template <typename> class Container>
typename Container<int>::iterator easyfind(Container<int> &container, int val)
{
#if 1 // Your code
typename Container<int>::iterator it = container.begin();
for ( ; it != container.end(); it++)
if (val == *it)
break ;
return it;
#else // code with <algorithm>
return std::find(container.begin(), container.end(), val);
#endif
}
Unfortunately, std::vector doesn't match Container, as it has extra template parameter (Allocator, which is defaulted).
You might add overload:
template <template <typename, typename> class Container, typename Alloc>
typename Container<int, Alloc>::iterator easyfind(Container<int, Alloc> &container, int val)
C++11 would allow template <template <typename...> class Container.
Simpler would be to use container directly as type:
template <typename Container>
#if 1 // No SFINAE
typename Container::iterator
#else // SFINAE with traits from C++11, which can be written trivially in C++98
typename std::enable_if<std::is_same<int, typename Container::value_type>>::type
#endif
easyfind(Container& container, int val)
{
#if 1 // Your code
typename Container::iterator it = container.begin();
for ( ; it != container.end(); it++)
if (val == *it)
break ;
return it;
#else // code with <algorithm>
return std::find(container.begin(), container.end(), val);
#endif
}
but more generic would be to drop int requirement completely:
template <typename Container>
typename Container::iterator
easyfind(Container& container, typename Container::const_reference val)
{
return std::find(container.begin(), container.end(), val);
}

Using vector iterator in function prototype

I want to do the following:
vector<int> vec;
auto iter = vec.begin();
Now I want a function which takes iter as an input and returns iter.
____ func(___ & iter)
where ___ is to be filled in. What should ___ be?
std::vector<int>::begin() returns an instance of type std::vector<int>::iterator, or std::vector<int>::const_iterator if the callee is marked const.
In your example, decltype(iter) will be std::vector<int>::iterator, so just declare the function like so:
std::vector<int>::iterator
func (std::vector<int>::iterator iter);
Note that STL iterators are designed to be passed by value, so I got rid of the reference in the function signature.
A bit silly example, but here is some examples of using templates and decltype to deduce type of iterators:
#include <vector>
#include <iostream>
using std::vector;
using std::cout;
template <class T> void double_vals(T vbeg, T vend)
{
for (; vbeg != vend; ++vbeg)
*vbeg *= 2;
}
template <class T, class T2> auto find_val(T vbeg, T vend, T2 val) -> decltype(vbeg)
{
for (; vbeg != vend; ++vbeg)
if (*vbeg == val) return vbeg;
return vbeg;
}
int main()
{
vector<int> vec = {10, 20, 30};
auto it1 = vec.begin();
auto it2 = vec.end();
double_vals(it1, it2);
auto it3 = find_val(it1, it2, 20);
if (it3 != vec.end())
cout << "Found value!\n";
for (auto i : vec)
{
cout << i << '\n';
}
return 0;
}
// Note that you could avoid using decltype() here by doing
template <class T, class T2> T find_val(T vbeg, T vend, T2 val)
{
for (; vbeg != vend; ++vbeg)
if (*vbeg == val) return vbeg;
return vbeg;
}
That actually depends on the iterator type you need. In general it will be vector<int>::iterator.
begin() is defined as:
iterator begin() noexcept;
const_iterator begin() const noexcept;

How to overload a template function to match specific containers?

I have the following function to retrieve the n element of a container - O(n):
template<typename Container>
const typename Container::value_type& getNthElement(const Container& container, size_t n) {
auto itr = cbegin(container);
for (auto i = 0u; i < n; ++i) {
++itr;
}
return *itr;
}
And for vectors I have this overload - O(1):
template<typename T>
T getNthElement(const vector<T>& container, size_t n) {
return container[n];
}
Now if I wanna use a deque (which also has the O(1) implementation), the first template function will be called with the O(n) implementation.
How can the second overload function be adapted to works for vectors and deques ?
My question is taken from this article.
The simple approach is to tag-dipatch based on the iterator category, i.e., something like this:
template <typename It>
typename std::iterator_traits<It>::value_type
nth_element(It begin, It end, std::size_t n, std::input_iterator_tag) {
for (std::size_t i(0); it != end && i != n; ++i) {
++i;
}
return it != end? *it: throw std::runtime_error("out of range");
}
template <typename It>
typename std::iterator_traits<It>::value_type
nth_element(It begin, It end, std::size_t n, std::random_access_iterator_tag) {
return n < std::size_t(end - begin)? it[n]: std::runtime_error("out of range");
}
template <typename C>
typename C::value_type
nth_element(Container const& c, std::size_t n) {
return nth_element(c.begin(), c.end(), n,
typename std::iterator_traits<C>::iterator_category());
}
If it weren't for n possibly being too big, you could actually just have std::advance() do the trick:
template <typename C>
typename C::value_type
nth_element(Container const& c, std::size_t n) {
auto it = c.begin();
std::advance(it, n);
return *it;
}
With C++11 extended SFINAE you can sniff-out whether this capability is available even without traits.

moving elements in a vector is not working as expected

I'm trying to move each element that has x value to the beginning of vector so that all the element that has x value is at the front of the vector, but it is not working , so can you tell me what I've done wrong, please?
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator it = find(c.begin(), c.end(), x);
if (it!=c.end()) {
c.insert(c.begin(), *it);
remove (it, c.end(), x);
}
}
int main()
{
int x=1;
vector <int> v{1,2,4,6,7,1,3,1,1,8,9};
move_x(v, x);
for(auto i:v)
cout<<v[i];
return 0;
}
and I'm getting this output when I run it
411613848811
Once you insert into the container, the iterator is no longer valid
c.insert(c.begin(), *it); // This invalidates 'it'
remove (it, c.end(), x); // oops! trying to use invalid iterator
Using std::rotate provides better alternative, which doesn't invalidate the iterators:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typedef typename Container::iterator It;
It write_it = c.begin(), read_it = c.begin();
for (;;) {
It found_it = find(read_it, c.end(), x);
if (found_it==c.end()) break;
read_it = found_it;
++read_it;
std::rotate(write_it,found_it,read_it);
++write_it;
}
}
As long as you are dealing with simple items like ints, this is a good approach:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::reverse_iterator it = std::remove(c.rbegin(),c.rend(),x);
for (;it!=c.rend();++it) {
*it = x;
}
}
This is a fixed implementation, of what you had in your code:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator it = find(c.begin(), c.end(), x);
if (it!=c.end()) {
c.erase(it);
c.insert(c.end(), x);
}
}
One issue with your implementation is that insert anywhere but the end can cause a reallocation and regardless will invalidate anything after the inserted position. So by definition since you are inserting at the beginning it will not be valid.
The second issue is with cout<<v[i]; it should actually be cout<<i;.
A nicer implementation that uses reverse iterators and moves all xs. This one erases as it goes and keeps a count and then does count inserts when done. Using erase with reverse iterators is a little tricky:
template <typename Container, typename Arg>
void move_all_x(Container& c, Arg x)
{
unsigned int count = 0 ;
for( typename Container::reverse_iterator it = c.rbegin() ; it != c.rend(); )
{
if( *it == x )
{
c.erase(--(it++).base() ) ;
++count ;
}
else
{
++it ;
}
}
for( unsigned int i = 0; i < count; ++i )
{
c.insert(c.begin(), x) ;
}
}
You could use std::partition, which will move all elements in a range which meet a given predicate to the beginning of the range, and return an iterator to the first element which doesn't meet the predicate.
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator endrange =
std::partition(c.begin(), c.end(), [&x](Arg ele){ return ele == x; });
}
In this case, we're not using the return value but I think it would probably be useful.
The output is wrong. There should be for (auto i:v) cout << i; not v[i]. You would see the garbage with a right algorithm too
You need a loop to process all matches (or use count and insert). v defined as list<int>:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
for (auto it = find(c.begin(), c.end(), x);
it != c.end();
it = find(it, c.end(), x)) {
c.insert(c.begin(), x);
it = c.erase(it);
}
}

Iterate through STL sequence and associative containers using same code?

Let's say I'd like to write an algorithm that prints the value of each element in a container. The container could be a Sequence or Associative container (e.g. std::vector or std::map). In the case of a sequence, the algorithm would print the value_type. In the case of an associative type, the algorithm would print the data_type. How can I write my algorithm (only once!) so that it works with either one? Pretend that the algorithm is complex and that I don't want to repeat it for both sequence/associative versions.
For example:
template <class Iterator>
void printSequence(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << *it;
}
template <class Iterator>
void printAssociative(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << it->second;
}
template <class Iterator>
void printEither(Iterator begin, Iterator end)
{
// ????
}
The difference that you have between your two function templates is not a difference between associative containers and sequences but a difference in the part of the type that is stored.
To clarify, std::set is an associative container but would work with your printSequence function; the problem with map is not the fact that it is associative, but that the value_type is a pair an you are only interested on the second part.
The simplest thing to do is to abstract the dereferencing operation.
E.g. used like this:
#include <map>
#include <vector>
template< class X, class Y >
void test( const std::map<X, Y>& mp )
{
printEither( mp.begin(), mp.end(), MakeMapDerefence( mp ) );
}
template< class Y >
void test( const std::vector<Y>& vec )
{
printEither( vec.begin(), vec.end(), MakeSimpleDereference( vec ) );
}
Defined like this (there's a fair bit of boiler plate that's probably a boost one-liner):
template< class ReferenceType, class IteratorType >
struct SimpleDereference
{
ReferenceType operator() ( IteratorType i ) const
{
return *i;
}
};
template< class ReferenceType, class IteratorType >
struct MapDereference
{
ReferenceType operator() ( IteratorType i ) const
{
return i->second;
}
};
// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::const_reference
, typename Container::const_iterator >
MakeSimpleDereference( const Container& )
{
return SimpleDereference< typename Container::const_reference
, typename Container::const_iterator >();
}
// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::reference
, typename Container::iterator >
MakeSimpleDereference( Container& )
{
return SimpleDereference< typename Container::reference
, typename Container::iterator >();
}
// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< const typename Container::mapped_type&
, typename Container::const_iterator >
MakeMapDerefence( const Container& )
{
return MapDereference< const typename Container::mapped_type&
, typename Container::const_iterator >();
}
// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< typename Container::mapped_type&
, typename Container::iterator >
MakeMapDereference( Container& )
{
return MapDereference< typename Container::mapped_type&
, typename Container::iterator >();
}
#include <iostream>
#include <ostream>
template <class Iterator, class Dereference> void printEither(Iterator begin, Iterator end, Dereference deref)
{
for (; begin != end; ++begin)
{
std::cout << deref(begin);
}
}
I've whipped up an iterator adapter based on Charles' answer. I'm posting it here in case anyone finds it useful:
#include <iostream>
#include <map>
#include <vector>
#include <boost/iterator/iterator_adaptor.hpp>
//------------------------------------------------------------------------------
template <class Iterator>
void print(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << *it << "\n";
}
//------------------------------------------------------------------------------
template <class BaseIterator>
class MapDataIterator :
public boost::iterator_adaptor<
MapDataIterator<BaseIterator>,
BaseIterator,
typename BaseIterator::value_type::second_type >
{
public:
typedef typename BaseIterator::value_type::second_type& reference;
MapDataIterator() {}
explicit MapDataIterator(BaseIterator base)
: MapDataIterator::iterator_adaptor_(base) {}
private:
friend class boost::iterator_core_access;
reference dereference() const
{return this->base_reference()->second;}
};
//------------------------------------------------------------------------------
int main()
{
std::vector<int> vec;
vec.push_back(31);
vec.push_back(41);
std::map<int,int> map;
map[31] = 41;
map[59] = 26;
typedef MapDataIterator< std::map<int,int>::iterator > DataIter;
print( vec.begin(), vec.end() );
print( DataIter(map.begin()), DataIter(map.end()) );
}
This solution has the added advantage that the algorithm need not be aware of how to dereference the iterators. It is also reusable for any existing algorithm that expects a "data sequence".
I'm surprised this little critter doesn't already exist in Boost.