Implement partially const iterator - c++

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

Related

How can I return `map<K, V>::iterator`? (`map` class extends `std::map`)

I making a custom map class (kc::map) that extends std::map, and I want to add at_index(), and I need to return map<K, V>::iterator. But when I return map<K, V>, this error occurs: error: invalid use of incomplete type ‘class kc::map<K, V>’:
namespace kc
{
template<typename K, typename V> class map : public std::map<K, V>
{
public:
using std::map<K, V>::map;
map<K, V> get_value()
{
return *this;
}
map<K, V>::iterator at_index(int index)
{
map<K, V> m = get_value();
for (int i = 0; i < index; i++)
{
m.erase(m.begin());
}
return m.begin();
}
};
};
You could return a std::map<K,V>::iterator. This would make your code compile, but the iterator would be invalid. get_value returns a copy, m is local to at_index and gets destroyed when the function returns.
Inheriting publicly from standard containers is rarely a good idea. Standard containers are not made to be inherited from (eg they do not have a virtual destructor). You can write at_index as an algorithm:
#include <map>
#include <iostream>
template <typename IT>
IT get_at_index(IT begin,IT end,unsigned index){
if (std::distance(begin,end) < index) return end;
return std::next(begin,index);
}
int main() {
std::map<int,int> m{{1,1},{2,2},{3,3}};
auto it = get_at_index(m.begin(),m.end(),1);
if (it != m.end()) std::cout << it->first << " " << it->second << "\n";
it = get_at_index(m.begin(),m.end(),5);
if (it == m.end()) std::cout << "index 5 is out of range\n";
}
However, a std::map isnt meant to be accessed by index. It is rather expensive to advance a maps iterator. If you want a container of key-value pairs that you can access by index, consider to use a std::vector<std::pair<K,V>> instead.
Ignoring the real problems with the code (deriving from standard containers is not recommended and at_index returns a dangling iterator to the local object m) to get the code to compile you have two options.
within a class you don't need to prefix members of the class with the class name. As iterator isn't a class member you need to add it first then an unqualified iterator will work:
using iterator = typename std::map<K, V>::iterator;
iterator at_index(int index)
You can just use std::map::iterator directly:
typename std::map<K, V>::iterator at_index(int index)
If what you're actually trying to do is get the ith item in a std::map this will work:
#include <map>
#include <stdexcept>
#include <iostream>
namespace kc
{
template<typename K, typename V> class map
{
private:
std::map<K, V> impl;
public:
using iterator = typename std::map<K, V>::iterator;
using value_type = typename std::map<K, V>::value_type;
std::pair<iterator, bool> insert( const value_type& value )
{
return impl.insert(value);
}
iterator at_index(int index)
{
if (index >= impl.size())
{
throw std::invalid_argument("index out of range");
}
auto it = impl.begin();
std::advance(it, index);
return it;
}
};
};
int main()
{
kc::map<int, int> m;
m.insert({1, 1});
m.insert({2, 2});
m.insert({3, 3});
std::cout << m.at_index(2)->second;
}

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.

how to write a template function which sort map by value?

I want to write a function, which sort map container by value.(not key, for key, i think map have done it)
i want to make it template, so it can sort map and unordered_map. by value of double or int or string.
my code is:
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;
template<typename T>
std::vector<typename T::value_type> sort_map(const T& m) {
std::vector<typename T::value_type> v(m.begin(), m.end());
std::sort(v.begin(), v.end(), [](const typename T::value_type& a, const typename T::value_type& b) { return a.second < b.second;});
return v;
}
int main() {
std::unordered_map<int, double> a = {{1, 6}, {2, 5}, {3, 5}, {4,3}};
auto v = sort_map(a);
for (auto & i : v) {
cout << "a[" << i.first << "] = " << i.second << endl;
}
}
I get a mess error code from my complier, which is such a mess, that i cant read.
but the error seems happens because i use the deleted operator = in pair.
Can anyone help on this?
The problem is the value_type of map and unordered_map is std::pair<const Key, Value>, note that the 1st element is const, which makes the vector containing std::pair<const Key, Value> can't be copied.
You can change the type of vector to std::vector<std::pair<typename T::key_type, typename T::mapped_type>>, .e.g
template<typename T>
auto sort_map(const T& m) {
std::vector<std::pair<typename T::key_type, typename T::mapped_type>> v(m.begin(), m.end());
std::sort(v.begin(), v.end(), [](const typename T::value_type& a, const typename T::value_type& b) { return a.second < b.second;});
return v;
}
LIVE

C++ generic insert into std container?

If I have the following program:
#include <vector>
#include <set>
template<class T, class U>
void AddToContainer(T& container, U value)
{
container.push_back(value);
}
int main(char**, int)
{
std::vector<int> v;
AddToContainer(v, 1);
std::set<int> s;
AddToContainer(s, 1);
return 0;
}
How can I make the adding to the container generic? Since std::set hasn't got a push_back but only insert, this will fail to compile.
You could use expression SFINAE with a dummy parameter to check if push_back() works:
template <class C, class V>
auto append(C& container, V&& value, int)
-> decltype(container.push_back(std::forward<V>(value)), void())
{
container.push_back(std::forward<V>(value));
}
template <class C, class V>
void append(C& container, V&& value, ...)
{
container.insert(std::forward<V>(value));
}
which your function will just forward to:
template <class C, class V>
void AddToContainer(C& container, V&& value) {
append(container, std::forward<V>(value), 0);
}
If push_back() is a valid expression, the first overload will be preferred since int is a better match for 0 than ... If push_back() isn't a valid expression, then there's only one viable overload.
Whether this is actually a good idea or not is a separate question.
I believe that all* C++ containers (though not the container adapters like priority_queue) have a version of insert that looks like this:
iterator insert(iterator location, T&& value)
For the sequence collections, the location is the actual location; for associative collections (like map and unordered_map), the iterator is a "hint" parameter (for instance, to help a map insert an element quickly if you already know precisely where it belongs in sorted order). However, providing an invalid hint doesn't cause any invalid behavior, so a valid generic insert for C++ collections would be:
template<C, T>
void insert(C& collection, T&& value) {
collection.insert(collection.end(), std::forward<T>(value));
}
* It looks like forward_list is the only one that doesn't have this method, which makes sense.
C++20 style:
template<typename C, typename V>
requires requires (C& c, V&& v) { c.push_back(std::forward<V>(v)); }
auto AddToContainer(C& container, V&& value) {
return container.push_back(std::forward<V>(value));
}
template<typename C, typename V>
requires (requires (C& c, V&& v) { c.insert(c.end(), std::forward<V>(v)); } &&
!requires(C& c, V&& v) { c.push_back(std::forward<V>(v)); })
auto AddToContainer(C& container, V&& value) {
return container.insert(container.end(), std::forward<V>(value));
}
or more succinct but with worse diagnostics:
template<typename C, typename V>
auto AddToContainer(C& container, V&& value)
{
if constexpr (requires (C& c, V&& v) { c.push_back(std::forward<V>(v)); })
return container.push_back(std::forward<V>(value));
else
return container.insert(container.end(), std::forward<V>(value));
}
If you are not worried about the order of elements inserted in the set,
template<typename Container, typename value>
void addelement(Container& C, value v)
{
std::fill_n(std::inserter(C,C.end()), 1,v);
}
int main()
{
std::vector<int> v;
addelement(v, 2);
addelement(v, 4);
addelement(v, 6);
std::set<int> s;
addelement(s, 8);
addelement(s, 6);
addelement(s, 4);
addelement(s, 8);
std::cout << "Vector elements :: " << std::endl;
for (auto item : v)
std::cout << item << std::endl;
std::cout << "Set elements :: " << std::endl;
for (auto item : s)
std::cout << item << std::endl;
return 0;
}

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