Create iterator for class using map - c++

I am implementing a class using a map, like so:
class Letras {
typedef std::pair<int, int> p_c;
std::map<char, p_c> bolsa;
public:
...
};
And I'd like to implement the functionality of an iterator, to be used like so:
Letras l;
Letras::iterator it;
for ( it = l.begin(); it != l.end(); ++it )
cout << *it << endl;
So that it prints out the chars from the std::map first component (key).
Thanks in advance.
More information about Letras
Letras is a class that implements a Scrubble-like letter set, the map is used to represent a set of letras (letters in Spanish) like the following:
{{letter_1, {quantity_1, score_1}}, {letter_2, {quantity_2, score_2}}, ..., {letter_n, {quantity_n, score_n}}}
Where letter_i is a certain letter from the set, quantity_i is the amount of times that letter is in the set (a Scrubble-like letter set can have various identical letters) and score_i is the letter score.
I have used a map because a letter can only have an associated score, so that in this class, i != j => letter_i != letter_j.
The utility of Letras is to find the best words formed by a certain letter set from a Diccionario class (dictionary), another class that has implemented a DAWG structure for fast search and lookup.

You can do something like this:
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
class Letras {
private:
typedef std::pair<int, int> p_c;
std::map<char, p_c> bolsa;
public:
class iterator {
private:
std::map<char, p_c>::iterator it;
public:
iterator(const std::map<char, p_c>::iterator &it) {
this->it = it;
}
// EDIT: Removing Copy Semantics
iterator(const iterator &it) = delete;
iterator &operator =(const iterator &it) = delete;
// EDIT: Enabling Move Semantics
iterator(iterator &&it) = default;
iterator &operator =(iterator &&it) = default;
auto &operator *() const {
return it->first;
}
iterator &operator ++(int) {
this->it++;
return *this;
}
bool operator !=(const iterator &it) {
return this->it != it.it;
}
};
auto begin() {
return iterator(bolsa.begin());
}
auto end() {
return iterator(bolsa.end());
}
void insert(const std::pair<char, p_c> &p) {
bolsa.insert(p);
}
};
int main() {
Letras b;
b.insert(make_pair('f', make_pair(1, 2)));
b.insert(make_pair('g', make_pair(1, 2)));
b.insert(make_pair('h', make_pair(1, 2)));
for (auto it = b.begin(); it != b.end(); it++) {
cout << *it << endl;
}
return 0;
}

Related

How can I properly reference the first vector container in my iterator?

Right now, I am trying to create a template class Set that holds a generic type <T> with an iterator. Although I don't quite understand what the purpose is, I'm suppoed to create what is called an "end sentinel" for the iterator as so:
while(start != end)
cout << start++ << endl;
start and end refer to what I believe to be the beginning and ending of the vector. So far, I've created a template class, created an iterator within it. In my main, I insert 10 integers and then try to use my "end sentinel". My code looks like this:
#include <iostream>
#include <vector>
using namespace std;
template <class T>
class Set{
vector<T> theSet;
public:
Set() {}
Set(const Set& s): theSet(s.theSet){}
~Set(){theSet.clear();}
void insert(T t){
cout << "inserted " << t << endl;
theSet.push_back(t);
}
class iterator;
friend class iterator;
class iterator{
Set<T>& s;
int index;
public:
iterator(Set<T>& is) : s(is), index(0) {}
iterator(Set<T>& is, bool) : s(is) {}
T operator*(){
return s.theSet.at(index);
}
T operator++(){
return ++s.theSet.at(index);
}
T operator++(int){
return s.theSet.at(index)++;
}
bool operator!=(const iterator& ri)const {return index!=ri.index;}
};
iterator begin() {return iterator (*this);}
//Create the end sentinel:
iterator end() {return iterator (*this, true); }
};
int main()
{
Set<int> other;
for(int i=0; i<10; ++i){
other.insert(i);
}
/*
for(Set<int>::iterator start = other.begin(); start != other.end(); start++){
cout << *start << endl;
}
cout << "\n\n Program ends succesfully" << endl;
*/
Set<int>::iterator start = other.begin();
Set<int>::iterator end = other.end();
while(start != end){
cout << start++ << endl;
}
return 0;
}
The problem comes when I reference the start and end at the end of my class:
iterator begin() {return iterator (*this);}
//Create the end sentinel:
iterator end() {return iterator (*this, true); }
It appears, begin() returns the iterator with the first constructor, and end() returns the iterator with the second constructor as the latter takes in two parameters. However, the second constructor I was given looks like
iterator(Set<T>& is, bool) : s(is) {}
I'm not sure how this references the "end" of the containers. How can I reference it in the correct way?
Here some example code. This is just for demonstration purposes! This code is not efficient and a lot of features are missing for the real world use!
#include <iostream>
using namespace std;
template <typename T>
class Set {
int sz;
T* elems;
public:
Set() : sz{0}, elems{nullptr} {}
Set(int n) : sz{n}, elems{new T[n]} {}
~Set() { delete[] elems; }
class Iterator;
Iterator begin() { return Iterator{elems}; }
Iterator end() {
return Iterator{elems + sz};
} // points to past-the-end of elems
// for example if sz=5:
// 1 2 3 4 5 | 6
// | |
// begin() end()
Iterator insert(T t) {
// check if the element t exists in the set
// -> if so, then return the pointer to the existing element
// -> otherwise return the pointer to the newly inserted element
// BUT:this is very inefficient !!!
// in a real world code one would use
// some kind of binary try for holding
// the keys/values of the set
for (int i = 0; i < sz; i++)
if (elems[i] == t)
return Iterator{elems+i};
T* new_elems = new T[sz + 1];
for (int i = 0; i < sz; ++i)
new_elems[i] = elems[i];
new_elems[sz++] = t;
delete[] elems;
elems = new_elems;
return Iterator{elems+sz};
}
class Iterator {
public:
Iterator() : p{nullptr} {}
Iterator(T* pp) : p{pp} {}
T* operator++() { return ++p; } // pre-increment, ex. ++iter
T* operator++(int) { return p++; } // post-increment, ex. iter++
T& operator*() { return *p; }
const T& operator*() const { return *p; }
bool operator==(const Iterator& rhs) const { return p == rhs.p; }
bool operator!=(const Iterator& rhs) const { return !this->operator==(rhs); }
private:
T* p;
}; // Iterator
}; // Set
int main() {
Set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
for (Set<int>::Iterator it=s.begin(); it != s.end(); ++it)
std::cout << *it << std::endl;
}
Here is the output:
https://wandbox.org/permlink/LT4F2CAkZhFCCcZw

Iterate over a std::vector in sorted order [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 9 years ago.
Improve this question
I receive from an API a vector of Foo as follows:
std::vector<Foo> foos;
I have then written a function called
std::vector<std::string> getKeys(const std::vector<Foo>&)
which iterates over the container and plucks out a key of type std::string for each Foo object.
How would you iterate over the Foo objects in foos in sorted order, where sorting is done on the key and in a case insensitive manner. Additionally, I'd prefer not to make a sorted copy of foos since it is large in size.
Here's my attempt, which works but I'm wondering if it can be done better.
struct CaseInsensitiveComparitor {
bool operator ()(const std::pair<std::string, Foo&> lhs, const std::pair<std::string, Foo&> rhs) const {
std::string str1 = lhs.first;
boost::algorithm::to_lower(str1);
std::string str2 = rhs.first;
boost::algorithm::to_lower(str2);
return (str1 < str2);
}
};
// map key to Foo
std::vector<std::pair<std::string, Foo*> > tempFoos;
{
std::vector<std::string> keys = getKeys(foos);
std::vector<std::string>::iterator begin = keys.begin();
std::vector<std::string>::iterator i = keys.begin();
std::vector<std::string>::iterator end = keys.end();
for(;i!=end;++i)
{
tempFoos.push_back(*i, &foos[distance(begin,i)]);
}
std::sort(tempFoos.begin(), tempFoos.end(), CaseInsensitiveComparitor());
}
std::vector<Foo*> sortedFoos;
std::vector<std::pair<std::string, Foo*> >::iterator i = tempFoos.begin();
std::vector<std::pair<std::string, Foo*> >::iterator end = tempFoos.end();
for(;i!=end;++i)
{
sortedFoos.push_back(i->second);
}
Alternatively to your attempt,
you may create an array of indexes
std::vector<size_t> indexes;
for (size_t i = 0; i != keys.size(); ++i) { indexes.push_back(i); }
using a comparator:
struct Comparator {
explicit Comparator(const std::vector<string>& keys) : keys(&keys) {}
bool operator ()(size_t lhs, size_t rhs) const {
std::string str1 = (*keys)[lhs];
boost::algorithm::to_lower(str1);
std::string str2 = (*keys)[rhs];
boost::algorithm::to_lower(str2);
return (str1 < str2);
}
private:
const std::vector<string>* keys;
};
sort this indexes array
std::sort(indexes.begin(), indexes.end(), Comparator(keys));
Now you can iterates foos and/or keys with the indexes indirection:
std::vector<Foo*> sortedFoos;
for (size_t i = 0; i != indexes.size(); ++i) {
sortedFoos.push_back(&foos[indexes[i]]);
}
You care currently iterating over foos three times and sorting it once. This is what will be making your algorithm less performant over large arrays. Why not change it to do the following
iterate over it to extract the pointers into a std::vecotr<Foo*> called fooPtrVec
Change your comparison function to dereference a Foo* and use the key field on Foo for the comparison. Call the function YourNewComparisonFunction
use std::sort(fooPtrVec.begin(), fooPtrVec.end(), YourNewComparisonFunction()) to sort the vector of Foo*
for(;i!=end;++end)
you have to increment your i not your end!
You can use a set to sort keys for you, and encapsulate them in a custom container for a more convenient usability:
class Foo
{
public :
Foo(const std::string & key) : key(key) {}
const std::string & get_key() const { return key; }
private :
std::string key;
};
std::ostream & operator<<(std::ostream & stream, const Foo & foo) { stream << foo.get_key(); return stream; }
class SortedFoo
{
typedef std::set<std::pair<std::string,Foo*> > SortedFoos;
SortedFoos mFoos;
public :
SortedFoo(std::vector<Foo> & foos)
{
const std::vector<Foo>::iterator end = foos.end();
for(std::vector<Foo>::iterator iter = foos.begin(); iter != end; ++iter)
{
mFoos.insert(std::make_pair(boost::algorithm::to_lower_copy(iter->get_key()), &(*iter)));
}
}
class Iterator : public std::iterator<std::forward_iterator_tag, Foo>
{
private:
Iterator(SortedFoos::iterator iter) : mIter(iter) {}
SortedFoos::iterator mIter;
public :
Iterator & operator ++ () { ++mIter; return *this; }
bool operator != (const Iterator & other) const { return mIter != other.mIter; }
Foo & operator * () { return *mIter->second; }
Foo * operator -> () { return mIter->second; }
friend class SortedFoo;
};
typedef Iterator iterator;
iterator begin() { return Iterator(mFoos.begin()); }
iterator end() { return Iterator(mFoos.end()); }
};
int main(int argc, const char** argv)
{
std::vector<Foo> foos;
foos.push_back(Foo("def"));
foos.push_back(Foo("Jkl"));
foos.push_back(Foo("yz "));
foos.push_back(Foo("pqr"));
foos.push_back(Foo("Mno"));
foos.push_back(Foo("ghi"));
foos.push_back(Foo("vwx"));
foos.push_back(Foo("Abc"));
foos.push_back(Foo("stu"));
SortedFoo sorted(foos);
std::copy(sorted.begin(), sorted.end(), std::ostream_iterator<Foo>(std::cout, " "));
return 0;
}
If you have duplicate keys, you can't use a set. You can replace it by a vector with only a few modifications:
typedef std::vector<std::pair<std::string,Foo*> > SortedFoos;
//...
SortedFoo(std::vector<Foo> & foos)
{
const std::vector<Foo>::iterator end = foos.end();
for(std::vector<Foo>::iterator iter = foos.begin(); iter != end; ++iter)
{
mFoos.push_back(std::make_pair(boost::algorithm::to_lower_copy(iter->get_key()), &(*iter)));
}
std::sort(mFoos.begin(), mFoos.end());
}
//...

comparing two different boost::iterator_facade iterators

I have two iterators, both derive from boost::iterator_facade (but not from each other) and I want to be able to compare them because I don't want to have more end() methods when one is sufficient. Is it possible?
The following minimal example is not working for me, but I think it should. What am I doing wrong?
#include <vector>
#include <iostream>
#include <boost/iterator/iterator_facade.hpp>
using namespace std;
typedef vector<int> val_type;
typedef vector<val_type> vec_type;
class myiterator
: public boost::iterator_facade<
myiterator
, val_type
, boost::forward_traversal_tag
>
{
private:
friend class boost::iterator_core_access;
friend class base_myiterator;
public:
explicit myiterator(vec_type::iterator _it)
: it(_it)
{}
myiterator(myiterator const& other)
: it(other.it)
{}
private:
void increment() { ++it; }
bool equal(myiterator const& other) const
{
return (it == other.it);
}
val_type& dereference() const { return *it; }
vec_type::iterator it;
};
class base_myiterator
: public boost::iterator_facade<
base_myiterator
, val_type
, boost::forward_traversal_tag
>
{
private:
friend class boost::iterator_core_access;
public:
explicit base_myiterator(vec_type::const_iterator _it, val_type _base)
: base(_base),
it(_it)
{
idx.resize(base.size());
}
base_myiterator(base_myiterator const& other)
: base(other.base),
it(other.it)
{
idx.resize(base.size());
}
private:
void increment()
{
++it;
for (size_t i=0; i<base.size(); ++i)
{
idx[i] = base[i] + (*it)[i];
}
}
bool equal(base_myiterator const& other) const
{
return (it == other.it);
}
bool equal(myiterator const& other) const
{
return (it == other.it);
}
val_type const& dereference() const
{
return idx;
}
val_type base;
vec_type::const_iterator it;
val_type idx;
};
struct Sample
{
vec_type vec;
myiterator begin()
{
return myiterator(vec.begin());
}
base_myiterator begin(val_type const& base)
{
return base_myiterator(vec.begin(), base);
}
myiterator end()
{
return myiterator(vec.end());
}
};
int main ()
{
Sample s;
val_type val;
val.push_back(1); val.push_back(0);
s.vec.push_back(val);
val.clear(); val.push_back(0); val.push_back(1);
s.vec.push_back(val);
val.clear(); val.push_back(-5); val.push_back(5);
//for (myiterator it=s.begin(); it!=s.end(); ++it)
for (base_myiterator it=s.begin(val); it!=s.end(); ++it)
{
cout << (*it)[0] << " " << (*it)[1] << endl;
}
}
boost::iterator_facade checks if two iterators are interoperable before instantiating the relation operators.
It uses boost::type_traits::is_convertible to check if it is legal to convert one type of iterator into the other type. Thus if you add a constructor base_myiterator(myiterator const& other) it will work. It will use the special equal overload you provided and not do any conversion (i.e. the additional copy constructor won't be used, it just needs to be there).
Furthermore, the base_myiterator seems to operate as some sort of const_iterator (val_type const& reference() const). In this case you should pass val_type const as template parameter Value to iterator_facade as described here.

How do I loop over consecutive pairs in an STL container using range-based loop syntax?

How do I create a custom class to loop over consecutive pairs of items in a STL container using a range-based loop?
This is the syntax and output I want:
std::list<int> number_list;
number_list.push_back(1);
number_list.push_back(2);
number_list.push_back(3);
auto paired_list = Paired(number_list);
for (const auto & pair : paired_list) {
std::printf("The pair is (%d, %d)\n", *(pair[0]), *(pair[1]));
// or
//std::printf("The pair is (%d, %d)\n", *(pair.first), *(pair.second));
}
// output:
// The pair is (1, 2)
// The pair is (2, 3)
I know these (and more) are needed, but I can't figure it out:
template <class T>
class Paired {
???
class iterator {
???
}
iterator begin() {
...
}
iterator end() {
...
}
}
Don't worry about const modifiers.
No boost.
Do not modify or copy objects in the container.
Here's what I would do.
#include <iterator>
#include <utility>
template <typename FwdIt> class adjacent_iterator {
public:
adjacent_iterator(FwdIt first, FwdIt last)
: m_first(first), m_next(first == last ? first : std::next(first)) { }
bool operator!=(const adjacent_iterator& other) const {
return m_next != other.m_next; // NOT m_first!
}
adjacent_iterator& operator++() {
++m_first;
++m_next;
return *this;
}
typedef typename std::iterator_traits<FwdIt>::reference Ref;
typedef std::pair<Ref, Ref> Pair;
Pair operator*() const {
return Pair(*m_first, *m_next); // NOT std::make_pair()!
}
private:
FwdIt m_first;
FwdIt m_next;
};
template <typename FwdIt> class adjacent_range {
public:
adjacent_range(FwdIt first, FwdIt last)
: m_first(first), m_last(last) { }
adjacent_iterator<FwdIt> begin() const {
return adjacent_iterator<FwdIt>(m_first, m_last);
}
adjacent_iterator<FwdIt> end() const {
return adjacent_iterator<FwdIt>(m_last, m_last);
}
private:
FwdIt m_first;
FwdIt m_last;
};
template <typename C> auto make_adjacent_range(C& c) -> adjacent_range<decltype(c.begin())> {
return adjacent_range<decltype(c.begin())>(c.begin(), c.end());
}
#include <iostream>
#include <vector>
using namespace std;
void test(const vector<int>& v) {
cout << "[ ";
for (const auto& p : make_adjacent_range(v)) {
cout << p.first << "/" << p.second << " ";
}
cout << "]" << endl;
}
int main() {
test({});
test({11});
test({22, 33});
test({44, 55, 66});
test({10, 20, 30, 40});
}
This prints:
[ ]
[ ]
[ 22/33 ]
[ 44/55 55/66 ]
[ 10/20 20/30 30/40 ]
Notes:
I haven't exhaustively tested this, but it respects forward iterators (because it doesn't try to use operations beyond ++, !=, and *).
range-for has extremely weak requirements; it doesn't require all of the things that forward iterators are supposed to provide. Therefore I have achieved range-for's requirements but no more.
The "NOT m_first" comment is related to how the end of the range is approached. An adjacent_iterator constructed from an empty range has m_first == m_next which is also == last. An adjacent_iterator constructed from a 1-element range has m_first pointing to the element and m_next == last. An adjacent_iterator constructed from a multi-element range has m_first and m_next pointing to consecutive valid elements. As it is incremented, eventually m_first will point to the final element and m_next will be last. What adjacent_range's end() returns is constructed from (m_last, m_last). For a totally empty range, this is physically identical to begin(). For 1+ element ranges, this is not physically identical to a begin() that has been incremented until we don't have a complete pair - such iterators have m_first pointing to the final element. But if we compare iterators based on their m_next, then we get correct semantics.
The "NOT std::make_pair()" comment is because make_pair() decays, while we actually want a pair of references. (I could have used decltype, but iterator_traits will tell us the answer too.)
The major remaining subtleties would revolve around banning rvalues as inputs to make_adjacent_range (such temporaries would not have their lives prolonged; the Committee is studying this issue), and playing an ADL dance to respect non-member begin/end, as well as built-in arrays. These exercises are left to the reader.
edit I was using transform.
Use adjacent_difference.
The second version takes a binary function which transforms the two values into a new
(different) value:
string make_message(int first, int second) {
ostringstream oss;
oss << "The pair is (" << first << ", " << second << ")";
return oss.str();
}
We can now transform the adjacent pairs into a third range. We'll use the ostream_iterator to use cout like a range:
list<int> numbers;
//...
adjacent_difference(numbers.begin(), numbers.end(),
ostream_iterator<string>(cout, "\n"),
make_message);
2nd edit
I found a question on comp.lang.c++.moderated asking why there aren't more 'adjacent' functions in the standard library such as for_each_adjacent. The reply said they were trivial to implement using std::mismatch.
I think this would be a better direction to go than implementing a special adjacent iterator.
Try this.
#include <list>
#include <iostream>
template<class T, class TIter = typename T::iterator, class TVal = typename T::value_type>
class PairedImpl {
T& m_t;
public:
class Iter {
TIter m_it;
public:
Iter(const TIter & it) : m_it(it) {}
bool operator!=(const Iter& it) { return m_it != it.m_it; }
Iter& operator++() { ++m_it; return *this; }
const Iter & operator *() const { return *this; }
const TVal & first() const { return *m_it; }
const TVal & second() const { return *std::next(m_it); }
};
PairedImpl(T& t) : m_t(t) {}
Iter begin() { return Iter(m_t.begin()); }
Iter end() {
TIter end = m_t.end();
return Iter(m_t.empty() ? end : --end);
}
};
template<class T>
PairedImpl<T> Paired(T& t) {
return PairedImpl<T>(t);
}
Usage
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(2);
lst.push_back(3);
lst.push_back(4);
lst.push_back(5);
for (const auto & pair : Paired(lst)) {
std::cout << "(" << pair.first() << ", " << pair.second() << ")" << std::endl;
}
return 0;
}
Okay, an hour with no answers, I've come up with a solution which works. Note that this uses my own FixedLengthVector which is exactly what it sounds like.
template <class T>
class Grouped {
private:
// length of grouped objects
static const unsigned length_ = 2;
// hold pointer to base container to avoid comparing incompatible iterators
T * base_container_;
public:
// constructor
Grouped(T & base_container) :
base_container_(&base_container) {
}
// iterator
class iterator {
private:
// hold pointer to base container to avoid comparing incompatible iterators
T * base_container_;
// hold pointers to objects in base container
FixedLengthVector<length_, typename T::value_type *> ptr_;
// hold iterator to last object
typename T::iterator last_iterator_;
public:
// constructor
iterator(T & base_container, typename T::iterator & it)
: base_container_(&base_container),
last_iterator_(it) {
// set up pointers if possible
unsigned i = 0;
// check for end iterator
if (last_iterator_ == base_container_->end()) {
ptr_.fill(NULL);
return;
}
// set up first object
ptr_[0] = &*last_iterator_;
// set up next objects
for (unsigned i = 1; i < length_; ++i) {
++last_iterator_;
if (last_iterator_ == base_container_->end()) {
ptr_.fill(NULL);
return;
}
ptr_[i] = &*last_iterator_;
}
}
// dereference operator
FixedLengthVector<length_, typename T::value_type *> & operator * (void) {
assert(ptr_[0] != NULL);
return ptr_;
}
// pre-increment
iterator & operator ++ (void) {
// can't increase past end
assert(last_iterator_ != base_container_->end());
// find next iterator
++last_iterator_;
if (last_iterator_ == base_container_->end()) {
ptr_.fill(NULL);
return * this;
}
// cycle pointers left
for (unsigned i = 1; i < length_; ++i) {
ptr_[i - 1] = ptr_[i];
}
ptr_[length_ - 1] = &*last_iterator_;
return * this;
}
// equality comparison
bool operator == (const iterator & that) const {
return base_container_ == that.base_container_ &&
last_iterator_ == that.last_iterator_;
}
// inequality comparison
bool operator != (const iterator & that) const {
return !(*this == that);
}
};
// end iterator
iterator end() {
return iterator(*base_container_, base_container_->end());
}
// begin iterator
iterator begin() {
return iterator(*base_container_, base_container_->begin());
}
};

boost multi index access to hashed unique index

This code is adopted from the boost multi-index "mru" example:
http://www.boost.org/doc/libs/1_46_1/libs/multi_index/example/serialization.cpp
I have code that is doing something similiar as a boost::unordered_map, but I would really like to add the mru functionality from this example.
I would like to make this code work as close to having a boost::unordered_map as possible. The key feature for me is the [] operator of the unordered_map.
The final lines of main() are broken and above each line as a comment is my question.
Thanks in advance to all answers comments.
#include <algorithm>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <iostream>
using namespace boost::multi_index;
class my_struct {
public:
my_struct(int in_a, std::string in_b) : a(in_a), b(in_b) {}
int a;
std::string b;
bool operator==(const my_struct &rhs) const
{
return (a == rhs.a);
}
bool operator!=(const my_struct &rhs) const
{
return !(*this == rhs);
}
friend std::ostream& operator<<(std::ostream &out, const my_struct&ms);
};
std::ostream& operator<<(std::ostream &out, my_struct &ms)
{
out << ms.a << " " << ms.b << std::endl;
return out;
}
inline std::size_t
hash_value(const my_struct &val)
{
return boost::hash_value(val.a);
}
// tags for multi_index
struct umap {};
template <typename Item>
class mru_list
{
typedef multi_index_container<
Item,
indexed_by<
sequenced<>,
hashed_unique<boost::multi_index::tag<umap>, identity<Item> >
>
> item_list;
public:
typedef Item item_type;
typedef typename item_list::iterator iterator;
mru_list(std::size_t max_num_items_):max_num_items(max_num_items_){}
void insert(const item_type& item)
{
std::pair<iterator,bool> p=il.push_front(item);
if(!p.second){ /* duplicate item */
il.relocate(il.begin(),p.first); /* put in front */
}
else if(il.size()>max_num_items){ /* keep the length <= max_num_items */
il.pop_back();
}
}
iterator begin(){return il.begin();}
iterator end(){return il.end();}
//private:
item_list il;
std::size_t max_num_items;
};
int main()
{
mru_list<my_struct> mru(10);
my_struct one(1, "One");
mru.insert(one);
mru.insert(my_struct(2, "Two"));
mru.insert(my_struct(3, "Three"));
mru.insert(one);
std::cout<<"most recently entered terms:"<<std::endl;
for (mru_list<my_struct>::iterator itr = mru.begin(); itr != mru.end(); ++itr) {
std::cout << itr->a << std::endl;
}
// what is my return type?
mru.il.get<umap>();
// Why doesn't this work?
mru_list<my_struct>::iterator itr = mru.il.get<umap>().find(one);
// Why doesn't this have a [] operator like boost:unordered_map
mru.il.get<umap>()[1] = "foobar";
return 0;
}
// what is my return type?
mru.il.get<umap>();
Its return type is the type of your umap index, which is:
typedef typename boost::multi_index::index<
item_list
, umap
>::type hashed_index_t;
mru_list<my_struct>::hashed_index_t& hashed_index = mru.il.get<umap>();
In C++11 it is easier with auto:
auto& hashed_index = mru.il.get<umap>();
// Why doesn't this work?
mru_list<my_struct>::iterator itr = mru.il.get<umap>().find(one);
find() returns an iterator of umap (second) index and the above statement is assigning it to the iterator of the first index. There are projection operations to convert from one index iterator type to another iterator type of the same multi-index container, e.g.:
mru_list<my_struct>::iterator itr = project<0>(mru.il, hashed_index.find(one));
// Why doesn't this have a [] operator like boost:unordered_map
Can't say why, it just doesn't.