How to declare an iterator variable for unknown container - c++

How to declare an iterator for unknown STL container? for example, I want to write a function that recieve container and print it all using iterators:
template <class Container> void print(Container c) {
// how to declare iterator???????
my_iterator = c.begin();
while(my_iterator!=c.end()) {
cout << *my_iterator << endl;
my_iterator++;
}
}

In C++03, you would need to get the iterator type from the container type explicitly:
typename Container::iterator it;
typename Container::const_iterator cit;
In C++11, you can just use auto:
auto my_iterator = c.begin(); // iterator in this case
auto my_iterator = c.cbegin(); // const_iterator always
Also note, as suggested my #Matthieu, that you can use a range based for loop in C++11, to simplify the code:
template <class Container>
void print(const Container& c)
{
for (const auto& elem : c)
cout << c << endl;
}

Go for:
for (auto& var : container)
{
cout << var << endl;
}
To display each elements of your container (and of any other type of container, even string or vector or map , ...)

Related

How do I go about calling this template function?

I have been searching for a method to convert a constant iterator to an iterator and I stumbled upon this StackOverflow question:
How to remove constness of const_iterator?
An answer suggested to use this template function:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
What I am confused about is how to go about implementing (calling) this function within my main code. I plan to use this code to convert a CGAL Ccb_halfedge_const_iterator to its non-constant counterpart. If the name of my Ccb_halfedge_const_iterator is dcel_circulator, how would I call this function:
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
within my main code?
#include <iterator>
#include <vector>
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
int main()
{
std::vector<int> vec = {};
std::vector<int>::const_iterator iter = vec.begin();
std::cout << std::is_same_v<decltype(iter), std::vector<int>::const_iterator> <<" - "
<< std::is_same_v<decltype(iter), std::vector<int>::iterator>
<<std::endl;
auto iter2 = remove_constness(vec, iter);
std::cout << std::is_same_v<decltype(iter2), std::vector<int>::const_iterator> << " - "
<< std::is_same_v<decltype(iter2), std::vector<int>::iterator>
<< std::endl;
}
If you have access to the container, why not just reproduce a new iterator to the same place. aka something like:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
auto out_it = std::begin(c);
std::advance(out_it, std::distance(std::cbegin(c), it));
return out_it;
}
Caveat: Wont work for reverse iterators, but typename Container::iterator excludes reverse iterators anyway.

Generic loop that iterates on both stl map and list (c++)

Is there any way to write a generic loop whcih iterates over values of both say stl map (associative container) and list (non associateve container).
template<typename T>
void foo(T &t)
{
for (auto iter = t.begin(); iter != t.end(); ++iter)
{
printf("%d\n", *iter); // will work for std::list<int> but not for std::map<int, int>
}
}
Thanks
To make it work for std::map - use proper adapter from boost:
foo(someMap | boost::adaptors::map_values);
You might also use Eric Niebler's ranges-v3
foo(someMap | ranges::values);
If you cannot use boost/ranges - use some kind of traits:
template <typename ValueType>
struct ValueGetter
{
static Value& get(ValueType& value)
{
return value;
}
};
template <typename Key, typename Value>
struct ValueGetter<std::pair<const Key, Value>>
{
using ValueType = std::pair<const Key, Value>;
static Value& get(ValueType& value)
{
return value.second;
}
};
template <typename ValueType>
auto& getValue(ValueType& value)
{
return ValueGetter<Value>::get(value);
}
template<typename T>
void foo(T &t)
{
for (auto iter = t.begin(); iter != t.end(); ++iter)
{
printf("%d\n", getValue(*iter));
}
}
Actually, there is already std::for_each (#include <algorithm>) for such purposes. You could feed it with appropriate handler, e. g. in the form of a lambda:
std::vector<int> v;
std::map<int, double> m;
std::for_each(v.begin(), v.end(), [](auto i) { printf("%d\n", i); });
std::for_each(m.begin(), m.end(), [](auto const& i) { printf("%d %f\n", i.first, i.second); });
I do like the way how Aconcagua show it and in most cases, this would be my favorite choice.
To make it more clear and fill the gap about my comment. The author's loop posted here is ok. In this case, the only problematic thing was about the printf(). To solve it, I have suggested something like overloading stream operator() to be able to print out std::map
ostream& operator<<(ostream& os, pair<string, int> it){
os << it.first << " => " << it.second;
}
as you might have realized the iterator in case of the std::map is the pair. Notice, you do not need to specify that for the std::list and the generic loop might look like the following
template<typename T>
void foo(T &t){
for(auto it=t.begin(); it=t.end(); ++it){
cout << *it << endl;
}
}
which is just fine with what was in the question, except printf() => cout. Here, you can also use range-based loops
template<typename T>
void foo(T &t){
for(auto it : t)
cout << it << endl;
}
and finally for_each() on the side with functor or lambda expression.

Pass a std container to a function

I came up with the following:
template <typename T> inline void printcontainer( std::vector<T> container )
{
for( auto it = container.begin(); it != container.end(); it++ )
{
std::cout << *it << std::endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<int> v;
v.push_back(5);
v.push_back(4);
v.push_back(3);
printcontainer(v);
return 0;
}
(Sorry for the push_backs, visual studio doesn't accept initializer lists...ugh!!)
now this function is limited to std::vector, how can I make it so that I can pass other containers, like std::list arrays etc...
Simply don't template on the type stored by the container, but on the type of the container itself:
template <typename Container>
inline void printcontainer(const Container &container)
Note that I changed the argument to const reference to avoid an unnecessary copy.
You can generalize your print function to C arrays by using the non-member std::begin and std::end or by using a range based for loop:
template <typename Container>
inline void printcontainer(const Container &container) {
for (const auto &v : container)
std::cout << v << "\n";
}
OT remark: You probably do not need the inline here.
Passing container objects around is against classic Stepanov's STL container-iterator-algorithm Generic Programming style.
Usually one would pass iterators:
# define ForwardIterator typename // workaround untill we have concepts
template <ForwardIterator It> inline void printcontainer( It begin, It end )
{
for(;begin != end; ++begin)
{
std::cout << *begin << std::endl;
}
}
Usage:
std::vector<int> v = {1, 2, 3, 4};
printcontainer(v.cbegin(), v.cend());

Implement partially const iterator

I'm implementing a template class of hash table with keys of type K and values of type V in C++11. All content of this table is stored in std::list <std::pair <K,V> >. Now I want to implement iterator and const_iterator for this table in a such way, that the client could modify only the second element of pair through iterator and couldn't modify any element of pair through const_iterator.
My first idea was just to use std::list <std::pair <const K,V> > as a storage and provide the appropriate iterator's of this list. The problem is when I declare this type of std::list, begin() and end() always return const_iterator, and when I try to convert it to iterator using this method, it doesn't work, because erase in libstdc++ receives only iterator as arguments (it's a bug of libstdc++). So I can't cast const_iterator to iterator.
How can I implement such iterators?
I assume here that you have good reasons to use std::list<std::pair<K, V>> for storage, and not std::[unoredered_]map<K, V> which already behaves like you say.
The simplest option is to use Boost's transform iterator:
https://godbolt.org/z/MvKezs8rx
#include<cassert>
#include<iostream>
#include<list>
#include<boost/iterator/transform_iterator.hpp>
template<class K, class V>
struct kv {
auto operator()(std::pair<K, V>& p) const {
return std::pair<K const&, V&>{p.first, p.second};
}
};
template<class Container, class N = typename Container::value_type, typename K = typename N::first_type, typename V = typename N::second_type>
auto begin_as_map(Container& c) {
return boost::make_transform_iterator(c.begin(), kv<K, V>{});
}
template<class Container, class N = typename Container::value_type, typename K = typename N::first_type, typename V = typename N::second_type>
auto end_as_map(Container& c) {
return boost::make_transform_iterator(c.end() , kv<K, V>{});
}
int main() {
std::list<std::pair<std::string, int>> storage = { {"house", 5}, {"car", 3} };
for(auto const& e : storage) {std::cout<< e.first <<" -> "<< e.second << std::endl;}
auto it = begin_as_map(storage);
assert(it->first == "house"); // ok, first is readable
assert(it->second == 5);
// it->first = "bla"; // error, first is not writable
it->second = 7;
std::cout<<" -- "<<std::endl;
for(auto const& e : storage) {std::cout<< e.first <<" -> "<< e.second << std::endl;}
}

C++ template specialization for map-like types

I want to define a generic function for printing contents of std::map like types. My initial attempt is a function like this:
template <class K, class V>
inline void PrintCollection(const std::map<K,V>& map,
const char* separator="\n",
const char* arrow="->",
const char* optcstr="") {
typedef typename std::map<K,V>::const_iterator iter_type;
std::cout << optcstr;
for (iter_type begin = map.begin(), it = begin, end = map.end();
it != end; ++it) {
if (it != begin) {
std::cout << separator;
}
std::cout << it->first << arrow << it->second;
}
std::cout << std::endl;
}
which works fine. When I try to generalize this function one more step, i.e. make it work for std::multimap type, compiler becomes angry. I tried several ways to make std::map generic in the function definition, such as:
template <class M, class K, class V>
inline void PrintCollection(const M<K,V>& map,
const char* separator="\n",
const char* arrow="->",
const char* optcstr="") {
typedef typename M<K,V>::const_iterator iter_type;
std::cout << optcstr;
for (iter_type begin = map.begin(), it = begin, end = map.end();
it != end; ++it) {
if (it != begin) {
std::cout << separator;
}
std::cout << it->first << arrow << it->second;
}
std::cout << std::endl;
}
with no success.
How can I generalize this function as I defined above?
To be more clear, I have already a function defined for vector-like classes defined before this function. It is like
template <class T>
inline void PrintCollection(const T& collection,
const char* separator="\n",
const char* optcstr="") {
typedef typename T::const_iterator iter_type;
std::cout << optcstr;
for (iter_type begin = collection.begin(), it = begin, end = collection.end();
it != end;
++it) {
if (it != begin) {
std::cout << separator;
}
std::cout << *it;
}
std::cout << std::endl;
}
So what I want to achieve it to make this function specialized to map-like classes. I'm pretty new in C++, so I don't know the exact term for this kind of stuff. Is this called "template specialization"?
Do it like the stdlib does and use iterators in your algorithm interfaces. This is the most generic solution.
template<class Iter>
void PrintCollection(Iter first, Iter last,
const char* separator="\n",
const char* arrow="->",
const char* optcstr="")
{
typedef Iter iter_type;
std::cout << optcstr;
for (iter_type begin = first, it = begin, end = last;
it != end; ++it) {
if (it != begin) {
std::cout << separator;
}
std::cout << it->first << arrow << it->second;
}
std::cout << std::endl;
}
int main()
{
vector<pair<int, int>> collection;
map<int, int> collection2;
pair<int, int> collection3[3];
PrintCollection(begin(collection), end(collection));
PrintCollection(begin(collection2), end(collection2));
PrintCollection(begin(collection3), end(collection3));
}
The answer is fairly simple.
There is no dependency on typenames K and V in the function. So remove them and make a general template. It can be used for both map and multimap:
template <class AnyMap>
void PrintCollection(const AnyMap& map,
...
{
typedef typename AnyMap::const_iterator iter_type;
On side note, with templates, you don't need inline keyword.
You could use a template-template parameter
template<template<class, class> class M, class K, class V>
inline void PrintCollection(const M<K, V>& map, /* rest as before */)
{
// rest as before
}
int main()
{
std::map<int, int> m1;
std::multi_map<int, int> m2;
// fill both maps
PrintCollection(m1);
PrintCollection(m2);
}
But as hansmaad is pointing out, you could also use a pair of iterators instead of the container as parameter. In general, you would prefer that solution if your PrintCollection is very generic and does not make use of the fact that it has a Key and Value type. OTOH, if your PrintCollection also needs to print that information in some future version, then you might want to use a template-template parameter that takes those two types as parameters.