How do I go about calling this template function? - c++

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.

Related

SFINAE applied to iterator

I'm writing a template function that should only accept random access iterator of any container containing a specific type (defined by template ).
At the moment, I'm first trying to limit the type of the iterator using SFINAE but the code does not compile.
#include <iostream>
#include <type_traits>
#include <vector>
template<typename It,
std::enable_if<
std::is_same<typename std::iterator_traits<It>::iterator_category,
std::random_access_iterator_tag>::value,
typename std::iterator_traits<It>::difference_type>>
void func(const It& begin, const It& end)
{
std::cout << begin[0] << std::endl;
}
int main()
{
std::vector<int> a = {0,1,2,3,4,5};
func(a.begin(), a.end());
return 0;
}
The error is:
error: ‘struct std::enable_if<std::is_same<typename
std::iterator_traits<_Iter>::iterator_category,std::random_access_iterator_tag::value,
typename std::iterator_traits<_Iterator>::difference_type>’ is not a
valid type for a template non-type parameter template<typename It,
std::enable_if<std::is_same<typename std::iterator_traits
error: no matching function for call to
‘func(std::vector<int>::iterator, std::vector<int>::iterator)’
func(a.begin(), a.end());
I can't parse your enable_if.
That works:
template<typename It, typename std::enable_if<std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0>
void func(const It& begin, const It& end)
{
std::cout << begin[0] << std::endl;
}
int main()
{
std::vector<int> a = {0,1,2,3,4,5};
func(a.begin(), a.end());
return 0;
}
But it can be that I misunderstood your intention.
You need typename = to use an anonymous type for SFINAE like that:
template<typename It,
typename = std::enable_if<std::is_same<typename std::iterator_traits<It>::iterator_category,
std::random_access_iterator_tag>::value,
typename std::iterator_traits<It>::difference_type>>
void func(const It& begin, const It& end)
{
std::cout << begin[0] << std::endl;
}
Or, you could use std::enable_if<...>::type as a return type for your function.
If you're working in C++17 or later, consider looking into the Concepts proposal that's making its way into C++20.

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;}
}

How to declare an iterator variable for unknown container

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 , ...)

Function template accepting nothing less than a bidirectional iterator or a pointer

I need a function template that accepts two iterators that could be pointers. If the two arguments are random_access iterators I want the return type to be an object of
std::iterator<random_access_iterator_tag, ...> type
else a
std::iterator<bidirectional_iterator_tag, ...> type.
I also want the code to refuse
compilation if the arguments are neither a bidirectional iterator, nor a pointer. I cannot have dependency on third party libraries e.g. Boost
Could you help me with the signature of this function so that it accepts bidirectional iterators as well as pointers, but not say input_iterator, output_iterator, forward_iterators.
One partial solution I can think of is the following
template<class T>
T foo( T iter1, T iter2) {
const T tmp1 = reverse_iterator<T>(iter1);
const T tmp2 = reverse_iterator<T>(iter2);
// do something
}
The idea is that if it is not bidirectional the compiler will not let me construct a reverse_iterator from it.
Here's an example with enable_if based on iterator tags. The substitution fails if the given T doesn't have a iterator_category typedef and so that overload isn't considered during overload resolution.
Since you can't use C++11, see the reference pages for enable_if and is_same to see how you can implement it by yourself.
#include <iterator>
#include <type_traits>
#include <iostream>
#include <vector>
#include <list>
template<typename T>
typename
std::enable_if<
std::is_same<
typename T::iterator_category,
std::bidirectional_iterator_tag
>::value,
T
>::type
foo(T it)
{
std::cout << "bidirectional\n";
return it;
}
template<typename T>
typename
std::enable_if<
std::is_same<
typename T::iterator_category,
std::random_access_iterator_tag
>::value,
T
>::type
foo(T it)
{
std::cout << "random access\n";
return it;
}
// specialization for pointers
template<typename T>
T* foo(T* it)
{
std::cout << "pointer\n";
return it;
}
int main()
{
std::list<int>::iterator it1;
std::vector<int>::iterator it2;
int* it3;
std::istream_iterator<int> it4;
foo(it1);
foo(it2);
foo(it3);
//foo(it4); // this one doesn't compile, it4 is an input iterator
}
Live example.
As per #JonathanWakely's comment, we can get rid of specialization for pointers if we use std::iterator_traits. The typename T::iterator_category part then becomes
typename std::iterator_traits<T>::iterator_category
a bit simpler than previous answer, no dependency on std::enable_if:
namespace detail
{
template<class T>
T do_foo(T iter1, T iter2, std::random_access_iterator_tag t)
{
cout << "do_foo random_access" << endl;
return iter1;
}
template<class T>
T do_foo(T iter1, T iter2, std::bidirectional_iterator_tag t)
{
cout << "do_foo bidirectional" << endl;
return iter1;
}
}
template<class T>
void foo(T iter1, T iter2)
{
typename std::iterator_traits<T>::iterator_category t;
detail::do_foo(iter1, iter2, t);
}
int main (int argc, const char * argv[])
{
std::vector<int> v;
foo(v.begin(), v.end());
std::list<int> l;
foo(l.begin(), l.end());
return 0;
}
The solution also supports other iterator_categories derived from std::random_access_iterator_tag or std::bidirectional_iterator_tag (should there be any), while std::same<> checks for strict category equality.

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.