How to call const_iterator for non const class? [duplicate] - c++

This question already has answers here:
How to use two functions, one returning iterator, the other returning const_iterator
(2 answers)
Closed 9 years ago.
I read some other threads related to this issue but non offered a solution for my problem. I hope you guys can give me ideas or advices.
I'm trying to implement this class named Map. it should contain 2 iterators - iterator and const_iterator.
I have them implemented - iterator inherits from const_iterator, and in the Map class I have the following functions:
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
we were given an example file to see what is required to implement.
in there, there is the following code:
Map<std::string,int> msi;
...
// print map
for(Map<std::string,int>::const_iterator it = msi.begin(); it != msi.end(); ++it) {
// more stuff here
}
since msi is a non-const Map instance, msi.begin() calls to iterator begin() and not const_iterator begin() const, resulting in unintended behaviour.
Assuming the example file is okay, how do I make it so msi.begin() calls the correct const_iterator function? (considering it, the iterator, is of type const_iterator).
EDIT: regarding the talk about the auto-conversion, i decided to add my iterator classes, please point out my mistake.
class Map {
//...
public:
class const_iterator {
private:
Node* currNode;
public:
const_iterator(Node* cur_node = NULL) : currNode(cur_node) {}
const_iterator& operator++() {
currNode = currNode->next;
return *this;
}
const_iterator operator++(int) {
const_iterator old = *this;
++(*this);
return old;
}
bool operator!=(const_iterator const& curr) {
return !(*this == curr);
}
string operator*() {
// this might cause memory leak
string toString(this->currNode->key);
std::stringstream s;
int tmp = this->currNode->value;
s << tmp;
string secondString(s.str());
toString = toString + ":" + secondString;
return toString;
}
bool operator==(const_iterator const& curr) {
return this->currNode == curr.currNode;
}
void operator=(const_iterator target) {
this = target;
}
//void operator=(Node* target) {
// this->currNode = target;
//}
};
class iterator : public const_iterator {
private:
Node* currNode;
public:
iterator(Node* cur_node = NULL) : currNode(cur_node) {}
iterator& operator++() {
currNode = currNode->next;
return *this;
}
iterator operator++(int) {
iterator old = *this;
++(*this);
return old;
}
bool operator==(iterator const& curr) {
return *this == curr;
}
bool operator!=(iterator const& curr) {
return !(*this == curr);
}
string operator*() {
// this might cause memory leak
string toString(this->currNode->key);
std::stringstream s;
int tmp = this->currNode->value;
s << tmp;
string secondString(s.str());
toString = toString + ":" + secondString;
return toString;
}
void operator=(iterator target) {
this = target;
}
};
//..
}

C++11 standard containers add cbegin and cend for that purpose. Lacking that, you can obviously always cast your object to const& explicitly to get a const view on the object.
More fundamentally, however, there’s no reason why your iterator shouldn’t support an automatic conversion to const_iterator. Like that, you’d not need to change the client code at all. In fact, your code should already support this if, as you’ve said, iterator inherits from const_iterator.
However, the code you’ve posted contains several errors. Firstly, operator= is wrong, and you should have received an error for it. The corrected version is:
void operator=(const_iterator target) {
currNode = target.currNode;
}
More importantly, your inheritance makes no sense. True, you do inherit iterator from const_iterator but your code pretends that this never happened – iterator completely reimplements its parent class and doesn’t relate to it in any way.
iterator should rather look something like this:
class iterator : public const_iterator {
public:
iterator(Node* cur_node = NULL) : const_iterator(cur_node) {}
};
This requires of course that currNode is declared protected in const_iterator. That class is also completely useless (but so is yours, at the moment) since it doesn’t add any functionality to the const_iterator class. You need to implement an operator* which allows modifying its value. Your current code fundamentally doesn’t allow this since it returns a newly created string rather than (something akin to) a reference to a map value.
Furthermore, it’s unclear how the const_iterator class gets ahold of a non-const Node pointer in the first place. That shouldn’t be possible: after all, it gets the pointer from a const Map.

If you will define msi
const Map<std::string,int> msi;
instead of
Map<std::string,int> msi;
const version of begin() and end() will be invoked

Related

How to convert an iterator-like that has a `next()` methode to a regular `begin`/`end` iterator pair?

In the codebase I inherited, there is a class that look like an iterator (this isn’t the exact code, but the logic is similar).
template <class T>
struct IteratorLike {
T* next() &; // either return a pointer to a valid value or nullptr
};
The way you use it is very similar to the way you use Rust iterators:
IteratorLike<...> it = ...;
while(auto* item = it.next()) {
do_something(*item);
}
How do I convert it to make it compatible with C++ range-based for loop, algorithms, or range-v3? I’m using C++14 (gcc5.5 to be more precise), so I can’t have a sentinel type that is different from the type of the iterator itself.
So far it seems that the easiest way is to store both the iterator and the next value in my wrapper:
template <class T>
class MyIterator {
private:
IteratorLike<T> m_iter;
T* m_value;
public:
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
using iterator_category = std::input_iterator_tag;
reference operator*() const {
assert(m_value && "trying to read past the end of the iterator");
return *m_value;
}
pointer operator->() {
// I’m not sure the assert is needed here
assert(m_value && "trying to read past the end of the iterator");
return m_value;
}
// Prefix increment
MyIterator& operator++() {
m_value = m_iter.next();
return *this;
}
// Postfix increment
MyIterator operator++(int) {
MyIterator tmp = *this;
++(*this);
return tmp;
}
// used by `my_collection.begin()`
explicit MyIterator(IteratorLike<T> iter)
: m_iter{m_iter}
, m_value{this->self.next()}
{}
// missing operator == and operator != as well as the constructor
// used `my_collection.end()
};
However, I fail to understand what my_collection.end() should return (EDIT: I just check, I can’t default-initialize m_iter), nor how to have meaningful comparison operators.
Note: I’m basically trying to do the exact reverse of this.
Since IteratorLike isn't default constructible, but is obviously copy constructible, you could use the instance you have to construct your end() iterator too. Example:
// used by `my_collection.begin()`
explicit MyIterator(const IteratorLike<T>& iter) :
m_iter{iter},
m_value{m_iter.next()}
{}
// used by `my_collection.end()`
MyIterator(const IteratorLike<T>& iter, std::nullptr_t) :
m_iter{iter},
m_value{nullptr}
{}
bool operator!=(const MyIterator& rhs) const {
return m_value != rhs.m_value;
}
Then in my_collection:
template<typename T>
class my_collection {
public:
MyIterator<T> begin() { return MyIterator<T>{itlike}; }
MyIterator<T> end() { return {itlike, nullptr}; }
private:
IteratorLike<T> itlike;
};

std::reverse_iterator strange behaviour (UB?) [duplicate]

This question already has answers here:
Reference invalidation after applying reverse_iterator on a custom made iterator
(2 answers)
Closed 5 years ago.
I created a pseudo-container class (it does not contain any elements) and an iterator class for this container.
The following code always outputs '776' on my system (my compiler is GCC 5.4.0)
#include <iostream>
#include <iterator>
class Container
{
public:
class Iterator;
Container()=default;
Container::Iterator begin();
Container::Iterator end();
};
class Container::Iterator: public std::iterator<std::bidirectional_iterator_tag, size_t>
{
public:
Iterator(size_t n);
size_t& operator*();
Container::Iterator& operator--();
const Container::Iterator operator--(int);
bool operator==(const Container::Iterator& rhs) const;
bool operator!=(const Container::Iterator& rhs) const;
private:
size_t n_;
};
Container::Iterator Container::end()
{
return Iterator(777);
}
Container::Iterator::Iterator(size_t n):
n_(n)
{
}
size_t& Container::Iterator::operator *()
{
return n_;
}
Container::Iterator& Container::Iterator::operator--()
{
n_--;
return *this;
}
const Container::Iterator Container::Iterator::operator--(int)
{
Container::Iterator oldVal = *this;
n_--;
return oldVal;
}
int main()
{
Container cont;
std::reverse_iterator<Container::Iterator>revit(cont.end());
//as cppreference says, "For a reverse iterator r constructed from an iterator i, the relationship &*r == &*(i-1) is always true...", so I expect that the output must be the same as if I used instead next commented line, and it does so on my system
// auto it = cont.end(); it--; std::cout << *it << std::endl;
std::cout << *revit << std::endl;
return 0;
}
But when I use any online compiler (with C++11 support), this code outputs just '0' (except one Clang version, then the output is some 'random' big number)
I can't figure out, where is my error?
std::reverse_iterator::operator* is equivalent to
Iterator tmp = current; return *--tmp;
(where current is the underlying iterator wrapped by this instance of reverse_iterator).
*tmp returns a reference to the member of tmp - which goes out of scope and is destroyed when std::reverse_iterator::operator* returns, taking that member with it. Therefore, *revit returns a dangling reference; the subsequent attempt to use it exhibits undefined behavior.

Wrapping a legacy C API in C++ with write iterator support

I have a legacy C API to a container-like object (specifically, the Python C API to tuples) which I would like to wrap in a nice C++14 API, so that I can use an iterator. How should I go about implementing this?
Here's some more details. We have the following existing C API which cannot be changed:
Py_ssize_t PyTuple_GET_SIZE(PyObject *p);
PyObject* PyTuple_GET_ITEM(PyObject *p, Py_ssize_t pos);
void PyTuple_SET_ITEM(PyObject *p, Py_ssize_t pos, PyObject *o)
We want to create a class which allows you to get access to a read/write iterator on the elements in the tuple.
The forward read-only iterator is not too difficult to define. Here is what I have:
class PyTuple {
private:
PyObject* tuple;
public:
PyTuple(PyObject* tuple) : tuple(tuple) {}
class iterator {
// iterator traits
PyObject* tuple;
Py_ssize_t index;
public:
iterator(PyObject *tuple, Py_ssize_t index) : tuple(tuple), index(index) {}
iterator& operator++() { index++; return *this; }
iterator operator++(int) { auto r = *this; ++(*this); return r; }
bool operator==(iterator other) const { return tuple == other.tuple && index == other.index; }
bool operator!=(iterator other) const { return !(*this == other); }
PyObject* operator*() { return PyTuple_GET_ITEM(tuple, index); }
// iterator traits
using difference_type = Py_ssize_t;
using value_type = PyObject*;
using pointer = PyObject**;
using reference = PyObject*&;
using iterator_category = std::forward_iterator_tag;
};
iterator begin() {
return iterator(tuple, 0);
}
iterator end() {
return iterator(tuple, PyTuple_GET_SIZE(tuple));
}
}
However, I am not too sure how to support writes. I have to somehow make *it = pyobj_ptr work. Conventionally, this would be done by changing the type to PyObject*& operator*() (so that it gives an lvalue) but I can't do this because the tuple "write" needs to go through PyTuple_SET_ITEM. I have heard that you can use operator= to solve this case but I am not sure if I should use a universal reference (Why no emplacement iterators in C++11 or C++14?) or a proxy class (What is Proxy Class in C++), and am not exactly sure what the code should look like exactly.
What you're looking for is basically a proxy reference. You don't want to dereference into PyObject*, you want to dereference into something that itself can give you a PyObject*. This is similar to how the iterators for types like vector<bool> behave.
Basically, you want operator*() to give you something like:
class PyObjectProxy {
public:
// constructors, etc.
// read access
operator PyObject*() const { return PyTuple_GET_ITEM(tuple, index); }
// write access
void operator=(PyObject* o) {
PyTuple_SET_ITEM(tuple, index, o); // I'm guessing here
}
private:
PyObject* tuple;
Py_ssize_t index;
};

testing if my iterator is at the front of the list when decrementing

So I need to make try/catch for when the iterator is at the front of the list and the -- operator is used. I've tried checking to see if the iterator is equal to begin() but that doesn't seem to work.
template <typename E>
class List : public SLinkedList<E> {
public:
// NOTE THE DIFFERENT LETTER – IT IS ONLY USED HERE!
// Use E everywhere else! m
// For a nested class, methods are declared and defined *INSIDE*
// the class declaration.
template <typename I>
class Iterator {
public:
// Give List access to Iterator private fields.
friend class List<E>;
// These are the minimum methods needed.
E operator*() { return nodePosition->elem; } //dereference the iterator and return a value
Iterator<E> operator++() {
try {
if (nodePosition->next == nullptr)
throw OutsideList("\nIterator:Error: attempted acess position outside of list.");
nodePosition = nodePosition->next;
}
catch (OutsideList err) {
cout << err.getError() << endl;
}
return *this;
} //increment the iterator
Iterator<E> operator--() {
try {
if (nodePosition == llist.begin())
throw OutsideList("\nIterator:Error: attempted acess position outside of list.");
nodePosition = nodePosition->prev;
}
catch (OutsideList err) {
cout << err.getError() << endl;
}
return *this;
} //decrement the iterator
bool operator==(const Iterator<E> p) {
return (nodePosition == p)
} //test equality of iterators
bool operator!=(const Iterator<E> p) {
return (nodePosition != p.nodePosition);
} //test inequality of iterators
private:
// Constructors & destructor here since only want List class to access.
// List constructor called from List::begin(). Use initializer list or
// create class copy constructor and assignment overload.
Iterator(const List<E>* sl) : llist(sl) {
nodePosition = sl->head;
}
// Class fields.
const List<E>* llist; //give Iterator class a handle to the list
Node<E>* nodePosition; //abstracted position is a pointer to a node
}; /** end Iterator class **/
/* The Iterator class is now fully defined. The rest of these
statements must go AFTER the Iterator class or the compiler
won’t have complete information about their data types.
*/
// REQUIRED: While not necessary for the code to work, my test suite needs
// this defined. Create a less cumbersome name for Iterator<E>. Use
// anywhere you would have used List<E>::Iterator<E> in class List. Allows
// this syntax in main() -- List<int>::iterator instead of List<int>::Iterator<int>.
typedef typename List<E>::Iterator<E> iterator;
/*** All method declarations and fields for the List class go here.
Any method that returns an iterator must be defined here.
***/
iterator begin() const { //return an iterator of beginning of list
// Call iterator constructor with pointer to List that begin() was
// called with.
return iterator(this);
}
E back();
E pop_back();
void push_back(const E e);
iterator end() const {
iterator itr = iterator(this);
while (itr.nodePosition->next != nullptr) {
++itr;
}
++itr;
return itr;
}
void insert(int itr, E elem);
void erase(int itr);
};
bool operator==(const Iterator<E> p) {
return (nodePosition == p);
}
does not seem right. The main problem is that you are comparing nodePosition of the current object, the object on the LHS, with the object on the RHS.
You don't need Iterator<E>. Just Iterator is good enough.
There is no point in making the argument const Iterator. It can be just Iterator, or better const Iterator&.
Make the member function a const member function.
The comparison needs to be made with the corresponding nodePositions.
Here's what I think it should be:
bool operator==(const Iterator& p) const {
return (nodePosition == p.nodePosition);
}

How to realize a custom implementation of a std-like iterator?

I have written a very simple file managing database that basicly looks like this:
class FileDB
{
public:
FileDB(std::string dir) : rootDir(dir) { }
void loadFile(std::string filename, File &file) const;
void saveFile(std::string filename, const File &file) const;
private:
std::string rootDir;
}
Now I would like to iterate through all files contained in the database like using a std::iterator:
void iterateFiles()
{
FileDB filedb("C:\\MyFiles");
for (FileDB::iterator file_it = filedb.begin(); file_it != filedb.end(); ++file_it)
{
File f = *file_it;
// do something with file
}
}
I've read answers to similar questions, some suggesting to derive std::iterator, some to use std::iterator_traits, but I don't really understand how to do that. What can possibly go wrong when trying to implement a custom iterator? And what's a simple yet elegant way to do it?
EDIT:
Please don't consider using boost, my question is of more conceptual nature.
EDIT 2:
The FileDB works like this:
rootDir
foo1
bar1
foo1bar1_1.txt
foo1bar1_2.txt
bar2
foo1bar2_1.txt
foo1bar2_2.txt
foo2
fooN
barM
fooNBarM_x.txt
So basicly, I can find a file by its name.
As my container is not in memory, I don't have pointers to its data. So my idea was to store the file's path in the iterator. This way, I can implement operator== with a string comparison, as the paths should be unique. The iterator returned from fileDB.end() would be an empty string and operator* would call fileDB::loadFile() with its filepath.
My biggest concern is about operator++. Having the filename, I can find out the containing directory and search for the next file, but this is really ineffective. Any ideas on how to do that? Or am I completely wrong with my whole concept?
Writing iterators yourself is hardly ever pretty. The pretiest way to add an iterator to your classes is to reuse an existing one. You should be aware, that pointers for example are just as good as iterators in C++ so there are many ways to provide an iterator without actually having to write your own.
This is basically how C++ works in many ways. It tries to make the language expendable and simple for end users by putting a lot of burden on library writers. I.e. library writers can write all the unpretty stuff, so the end user doesn't have to. Iterators are usally a part of a library.
Having said that, here comes the actual ugly part:
To be able to write your own iterators, here are some things you need to be aware of.
Type traits:
Type traits are a simple mechanism to add aditional information to types in C++ which works even with types which cannot be changed themselves. For example for an iterator it is important to know what it iterates over (i.e. the contained type). The way to get this information for a given iterator depends a lot on the iterator. For iterators which actually are objects, you can add typedefs in the class and use those, but for iterators which are pointers you need to infer it from the pointer type. To make this possible the information is stored in a type trait instead so there is a single place an code can look this information. This is the std::iterator_traits type trait.
std::iterator_traits work on anything, which is derived from the std::iterator template as well as on any kind of pointer, without any tweaking. So often it is best to use std::iterator as a base to avoid having to write your own traits specialisation. In case you cannot do this, it is still possible to provide the necessary traits, but it will be harder.
Tag classes and iterator types:
There are several different types of iterators available in C++ which have different behaviour and can/cannot do a lot of different things. Have a look at http://cplusplus.com/reference/std/iterator/ to see what kind of iterators are available and what they can do. The diagrams are not meant to be in an object oriented way (i.e. an input_iterator is neither a sub nor a base class of a forward_iterator), but rather as an API kind of derivation. I.e. you can use all algorithms which were written for an input iterator also with an forward iterator. The table on the page will tell you which methods you have to provide for each category.
Since these categories are not actually sub classes of each other (they shouldn't be, especially when comming from different types of collections), another mechanism is used to identify the capabilities of each iterator. An empty tag class is also included in the std::iterator_traits describing each iterator, which tells what this iterator can do and what it cannot do. If you do not write your own traits, you need to supply this tag class to the std::iterator template upon instantiation.
Example:
This example is taken from the cplusplus.com section on iterators:
class myiterator : public iterator<input_iterator_tag, int>
{
int* p;
public:
myiterator(int* x) :p(x) {}
myiterator(const myiterator& mit) : p(mit.p) {}
myiterator& operator++() {++p;return *this;}
myiterator operator++(int) {myiterator tmp(*this); operator++(); return tmp;}
bool operator==(const myiterator& rhs) {return p==rhs.p;}
bool operator!=(const myiterator& rhs) {return p!=rhs.p;}
int& operator*() {return *p;}
};
This iterator does not really make sense, since it only wraps a pointer, which could also have been used directly. However it can serve as an explanation. The iterator is derived from std::iterator as an input_iterator by supplying the appropriate tag. Also the template is told, that this iterator is iterating over ints. All other types, which are needed difference_type, reference, poiner etc. are automatically infered by the template. In some cases it may make sense to change some of these types manually (for example a std::shared_ptr has to be used as a pointer sometimes). Also the traits needed for this iterator will exist automatically, since it is already derived from std::iterator and the std::iterator_traits know where to find all necessary information.
class FileDB
{
class iterator;
public:
FileDB(std::string dir) : m_rootDir(dir) { m_files = getFileTreeList(); }
void loadFile(std::string filename, File &file) const;
void saveFile(std::string filename, const File &file) const;
iterator begin()
{
return iterator(m_files.begin(), *this);
}
iterator end()
{
return iterator(m_files.end(), *this);
}
private:
std::list<std::string> getFileTreeList() const;
private:
std::string m_rootDir;
std::list<std::string> m_files;
}
class FileDB::iterator
{
public:
iterator(std::list<std::string>::iterator pos, FileDB& owner) : m_pos(pos), m_owner(owner){}
bool operator==(const iterator& rhs) {return m_pos == rhs.m_pos;}
bool operator!=(const iterator& rhs) {return m_pos != rhs.m_pos;}
//etc
void operator++() {++m_pos;}
File operator*()
{
return openFile(*m_pos); // for example open some file descriptor
}
~iterator()
{
closeFile(*m_pos); // clean up!
}
private:
std::list<std::string>::iterator& m_pos;
FileDB& m_owner;
};
Here is iterator which calculatetes subnodes during traversal.
I wrote it for windows, but I think it is not difficult to castomize it for other platforms.
#include <list>
#include <windows.h>
#include <assert.h>
#include <iostream>
#include <string>
class File{};
class Iterator
{
public:
virtual bool isDone() = 0;
virtual void next() = 0;
virtual std::string getFileName() = 0;
virtual ~Iterator(){};
};
bool operator== (Iterator& lhs, Iterator& rhs);
class EndIterator : public Iterator
{
public:
virtual bool isDone() {return true;}
virtual void next(){};
virtual std::string getFileName() {return "end";};
};
class DirectoryIterator : public Iterator
{
public:
DirectoryIterator(const std::string& path);
virtual bool isDone();
virtual void next();
virtual std::string getFileName();
virtual ~DirectoryIterator();
private:
std::list<Iterator*> getSubelementsList(const std::string& path) const;
void init();
private:
bool m_wasInit;
std::string m_path;
std::list<Iterator*> m_leaves;
std::list<Iterator*>::iterator m_current;
};
class FilesIterator : public Iterator
{
public:
FilesIterator(const std::string& fileName);
virtual bool isDone(){return true;};
virtual void next(){};
virtual std::string getFileName();
virtual ~FilesIterator(){};
private:
std::string m_fileName;
};
class DbItertor
{
public:
DbItertor(Iterator* iterator) : m_ptr(iterator){}
DbItertor(const DbItertor& rhs) {*m_ptr = *rhs.m_ptr;}
std::string operator*()
{
if(m_ptr->isDone())
return "end";
return m_ptr->getFileName();
}
//File operator->(){return FileOpen(m_ptr->getFileName());}
void operator++() {m_ptr->next();}
~DbItertor(){delete m_ptr;}
private:
Iterator* m_ptr;
};
class FileDB
{
public:
FileDB(std::string dir) : m_rootDir(dir){}
DbItertor begin()
{
return DbItertor(new DirectoryIterator(m_rootDir));
}
DbItertor end()
{
return DbItertor(new EndIterator());
}
private:
std::string m_rootDir;
};
FilesIterator::FilesIterator(const std::string& fileName) :
m_fileName(fileName)
{}
std::string FilesIterator::getFileName()
{
return m_fileName;
}
DirectoryIterator::DirectoryIterator(const std::string& path) :
m_wasInit(false),
m_path(path)
{}
void DirectoryIterator::init()
{
m_leaves = getSubelementsList(m_path);
m_current = m_leaves.begin();
m_wasInit = true;
next();
}
DirectoryIterator::~DirectoryIterator()
{
for(std::list<Iterator*>::iterator i = m_leaves.begin(); i != m_leaves.end(); ++i)
delete *i;
}
void DirectoryIterator::next()
{
if(!m_wasInit)
init();
if(isDone())
return;
if((*m_current)->isDone())
++m_current;
else
(*m_current)->next();
}
bool DirectoryIterator::isDone()
{
if(!m_wasInit)
init();
return (m_leaves.size() == 0) || (m_current == --m_leaves.end());
}
std::string DirectoryIterator::getFileName()
{
if(!m_wasInit)
init();
return (*m_current)->getFileName();
}
std::list<Iterator*> DirectoryIterator::getSubelementsList(const std::string& path) const
{
std::list<Iterator*> result;
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
char sPath[2048] = {0};
sprintf(sPath, "%s\\*.*", path.c_str());
hFind = FindFirstFile(sPath, &fdFile);
assert(hFind != INVALID_HANDLE_VALUE);
do
{
if(strcmp(fdFile.cFileName, ".") != 0 && strcmp(fdFile.cFileName, "..") != 0)
{
std::string fullName = path;
fullName += std::string(fdFile.cFileName);
if(fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
{
fullName += "\\";
result.push_back(new DirectoryIterator(fullName));
}
else
{
result.push_back(new FilesIterator(fullName));
}
}
}
while(FindNextFile(hFind, &fdFile));
FindClose(hFind);
return result;
}
bool operator== (Iterator& lhs, Iterator& rhs)
{
return lhs.getFileName() == rhs.getFileName();
}
bool operator!= (DbItertor& lhs, DbItertor& rhs)
{
return *lhs != *rhs;
}
int main()
{
FileDB db("C:\\456\\");
for(DbItertor i = db.begin(); i != db.end(); ++i)
{
std::cout << *i << std::endl;
}
return 0;
}