I'm trying to implement erase function to my square list function but when I write mine it give me this compiler error
Error 1 error C2664: 'square_list::erase' : cannot convert
parameter 1 from 'std::_List_iterator<_Mylist>' to 'int *'
main code
#define nullptr NULL
template <typename T_>
class square_list
{
void erase(iterator it)
{
data.erase(it);
}
};
std::list<T>::erase takes a list::iterator as argument, but you are trying to pass it a square_list<T>::iterator, a.k.a. a T*.
I would question the logic of defining iterators as T* for a squared_list<T>, while simultaneously exposing list<T>::iterators to the underlying list in the public interface (begin() and `end()1), as this breaks encapsulation.
Nevertheless, if you want to proceed, you can do something like this:
void erase(typename std::list<T>::iterator it)
{
data.erase(it);
}
Or, if you want to actually use the iterators that you define in your squared_list class:
void erase(iterator it)
{
// look for the underlying list iterator pointing to this value
auto underlyingIt = std::find_if(data.begin(), data.end(), [=](const T& node) {
return (&node == it);
});
if (underlyingIt != data.end()) {
data.erase(underlyingIt);
} else {
// handle case where the "iterator" is not found here
}
}
Make the erase function to take the std::list iterator, as follows:
void erase(typename std::list<value_type>::iterator it)
{
data.erase(it);
}
Live example: http://ideone.com/c3T9Hs
Related
I want to access my iterator class by reference
#include <iostream>
template <typename T> class binary_tree;
template <typename T>
class binary_tree_iterator {
private:
binary_tree<T>* tree;
T data;
public:
binary_tree_iterator(binary_tree<T>* t) : tree(t) {}
T& operator*() {data = tree->data(); return data;}
binary_tree_iterator& operator++() {tree = tree->get_node(); return *this;}
bool operator!=(binary_tree_iterator& rhs) {return tree->data() != rhs.tree->data();}
};
template <typename T>
class binary_tree {
private:
T t_data;
binary_tree<T>* node;
binary_tree_iterator<T>* It;
public:
binary_tree(T d) : t_data(d), node(nullptr), It(nullptr)
{}
T& data() {
return t_data;
}
void set_node(binary_tree<T>* node) {
this->node = node;
}
binary_tree<T>* get_node() {
return node;
}
binary_tree_iterator<T> begin() {
It = new binary_tree_iterator<T>(this);
return *It;
}
binary_tree_iterator<T> end() {
if(node == nullptr) {
It = new binary_tree_iterator<T>(this);
return *It;
} else {
return node->end();
}
}
};
int main() {
binary_tree<int>* tree = new binary_tree<int>(2);
tree->set_node(new binary_tree<int>(3));
//for(auto& x: *tree) <--- does not work
for(auto x: *tree) {
std::cout << x << std::endl;
}
}
The for-range loop I want to use it in looks something like for(auto& x: *tree). How do I give it a reference? Is there a standard way of doing this when creating iterators? When I return the data value I assign it to a iterator data member so I can return by reference. Will I have to do the same with my iterator? I don't imagine this is the standard way of doing this.
I want to access my iterator class by reference
How do I give it a reference?
By changing the return type of the function to be binary_tree_iterator<T>& instead of binary_tree_iterator<T>. If you do that, you'd have to store an iterator somewhere instead of returning a new one, so that you can refer to it. Presumably, it would have to be stored as a member variable.
Is there a standard way of doing this when creating iterators?
No. None of the standard containers return references to iterators.
I don't imagine this is the standard way of doing this.
Indeed. The "standard" i.e. conventional thing to do is to not return references to iterators.
The for-range loop I want to use it in looks something like for(auto& x: *tree)
There is no need to return a reference to an iterator in order to make that work. If you take a look at standard containers, you'll find that none of them return a reference to an iterator, and such loop works with all of them.
The reference in that loop is bound to the result of indirection through the iterator. So, it is the operator* that must return a reference to the pointed object. And, your operator* does indeed return a reference. That said, normally an iterator would return a reference to the object stored in the container; not a reference to copy stored in the iterator. So, that's highly unconventional.
Finish writing your iterator, and you'll find that the loop works.
In conclusion: You don't need to return iterator by reference, and you shouldn't.
I know how do erase elements of a list, and that erase return a valid iterater. My problem is, that I want to remove not just one element but multiple.
Actuall my code is like
for(list<Treenode*>::iterator it=currentLevel->begin(); it!=currentLevel->end(); ++it){
if(something(*it)) {
for(list<Treenode*>::iterator it2=currentNewLevel->begin();it2!=currentNewLevel->end();){
if (somethingDifferent(*it2)) {
it2=currentLevel->erase(it2);
} else {
++it2;
}
}
}
}
of course this could not work, because it is not changed. I don't know how to change the iterator but stay on this iteration-step.
Thank you for reading. I hope someone know the answer.
In general, the way to remove elements from a list based depending on a condition is to use the std::list::remove_if member function.
bool pred(const Treenode* t) { .... }
currentNewLevel.remove_if(pred);
The predicate can be a functor, so it can keep any state that is required to implement the removal criteria:
#include <algorithm> // for std::find_if
#include <list>
// unary predicate functor. Returns true if an element of a reference
// list satisfies "something" and the functor call argument satisfies "somethingDifferent"
struct Pred
{
Pred(const std::list<Treenode*>& nodes) : nodes_(nodes) {}
bool (const Treenode* t) const
{
return std::find_if(nodes_.begin(), nodes_.end(), something) != nodes_.end() &&
somethingDifferent(t);
}
private:
const std::list<Treenode*>& nodes_;
};
then
Pred p(currentLevel);
currentLevel.remove_if(p);
do while loop. it's mostly ++ too so the performance should be good.
std::vector<PdfTextRegion*>::const_iterator textRegion = m_pdfTextRegions.begin();
while(textRegion != m_pdfTextRegions.end())
{
if ((*textRegion)->glyphs.empty())
{
m_pdfTextRegions.erase(textRegion);
textRegion = m_pdfTextRegions.begin();
}
else
textRegion++;
}
I have a little problem while trying to implement a container, Set, based on a linked list.
Yeah, I know that there's an STL implementation of a set, but this is for homework. :)
So, this is what I have done so far:
my Set.h file looks like that:
template <class T>
class Set {
private:
typedef std::list<T> base_container;
base_container items;
public:
class myIterator {
public:
typename base_container::iterator base_iterator;
myIterator() { }
};
void addItem(const T item) {
items.push_back(item);
}
typedef typename Set<T>::myIterator setIterator;
setIterator begin() { return items.begin(); }
setIterator end() { return items.end(); }
Set<T>(void) { }
~Set<T>(void) { }
};
Now, main.cpp:
#include "Set.h"
int main(void) {
Set<int> mySet;
mySet.addItem(1);
mySet.addItem(2);
mySet.addItem(3);
mySet.addItem(4);
Set<int>::myIterator x;
x = mySet.begin(); // produces an error about non-convertible types.
return EXIT_SUCCESS;
}
The error is as follows:
error C2664: 'Set<T>::myIterator::myIterator(const Set<T>::myIterator &)' : cannot convert parameter 1 from 'std::_List_iterator<_Mylist>' to 'const Set<T>::myIterator &'
Clearly I messed things up, but I'm not sure which part of the code is actually the problem.
Any suggestions about how to fix this? Any helpful information will be appreciated.
Thanks. :)
There are many problems with your approach.
As others have said, you can't create your iterator type from the underlying type:
setIterator begin() { return items.begin(); }
setIterator end() { return items.end(); }
This can be solved by adding a constructor to your type:
class myIterator {
typedef typename base_container::iterator base_iterator_type;
public:
explicit myIterator(base_iterator_type i) : base_iterator(i) { }
base_iterator_type base_iterator;
myIterator() { }
};
This constructor should be explicit, which means you need to change how you create it:
setIterator begin() { return setIterator(items.begin()); }
The next problem is that your type doesn't implement the iterator interface, it doesn't provide operator++ or operator* etc. and it doesn't define nested types such as value_type and iteratory_category i.e. it's not an iterator (just giving it a name with "iterator" in it doesn't make it true!)
Once you fix that and your type is a valid iterator, you'll find your container can't be used with STL-style algorithms because it doesn't implement the container requirements. Among other things, it should provide a nested type called iterator not setIterator so that other template code can use S::iterator without caring if S is a std::set<T> or a Set<T>. Don't call it Set<T>::setIterator, just call it Set<T>::iterator. Also in this case there's no point defining myIterator (noone cares that it's yours! :-) then having a typedef to call it setIterator, just name the type with the right name in the first place and you don't need a typedef.
The problem is here:
setIterator begin() { return items.begin(); }
setIterator end() { return items.end(); }
The values you're trying to return have the wrong type. They are of type base_iterator and not setIterator, and there's no way to implicitly convert from the former to the latter.
You can derive from std::iterator<T> and provide the approprate iterator category tag. This will automatically give your iterator the required nested types such as value_type in terms of T. You will still need to write function members such as operator++ and operator* (depending on your iterator category, e.g. you also need operator[] for random access iterators)
As far as I can tell in c++ there is no common base class that covers both iterator and reverse_iterator.
The only suggestion I have seen so far is to get around this using templates (
How to write a function that takes an iterator or collection in a generic way? )
However this solution doesn't seem to work for me.
class MyClass
{
template<typename Iter> Iter* generate_iterator(...params...)
{
//returns either a vector::iterator or vector::reverse_iterator
}
template<typename Iter> void do_stuff(Iter *begin, Iter *end)
{
//does stuff between elements specified by begin and end
//I would like this function to remain agnostic of which direction it is working in!
}
void caller()
{
//I would like this function to remain agnostic of which direction it is working in too...
do_stuff(generate_iterator(blah),generate_iterator(foo));
}
};
In this case, generate_iterator() cannot be used as desired because the compiler complains "generate_iterator is not a member of class MyClass" presumably because I haven't specified it (which I can't in practice as caller should be agnostic of the iterator type).
Can anyone help? Thanks in advance!
edit: as Mark B pointed out generate_iterator must return a pointer - now corrected
update: just started using this http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/start_page.html and it seems to work...
You can create your own iterator class that knows how to go both directions. Encapsulate both types of iterator and internally select whichever one you were initialized with.
Here's a start:
template<typename Container>
class BiIterator
{
public:
BiIterator(Container::iterator i) : m_fwd(i), m_isforward(true) {}
BiIterator(Container::reverse_iterator i) : m_rev(i), m_isforward(false) {}
bool operator==(const BiIterator & left, const BiIterator & right);
Container::value_type & operator*()
{
if (m_isforward)
return *m_fwd;
return *m_rev;
}
const Container::value_type & operator*() const;
BiIterator & operator++()
{
if (m_isforward)
++m_fwd;
else
++m_rev;
return *this;
}
private:
Container::iterator m_fwd;
Container::reverse_iterator m_rev;
bool m_isforward;
};
In C++ you can't write a function that returns two different types. In your template case it will return one or the other depending on the instantiation. You could possibly return a base pointer to a polymorphic iterator but that would cause me to ask what you're really trying to do here. Even the standard containers don't try to do that: They have begin and rbegin to distinguish properly. I would suggest having two separate functions that each do the right thing and return one type of iterator or the other as context dictates.
As a side, note that you can't implicitly determine a template instantiation of a type that's only used for the return type of a function.
By using boost tuple and boost any , your problem can be easily solved. I wrote a example by using boost::any , see below:
#include <boost/any.hpp>
using boost::any_cast;
#define MSG(msg) cout << msg << endl;
boost::any getIterator(std::vector<int>& vec, bool bReverse)
{
if(!bReverse)
return boost::any(vec.begin());
else
return boost::any(vec.rbegin());
}
int main()
{
std::vector<int> myvec;
myvec.push_back(1);
myvec.push_back(2);
myvec.push_back(3);
typedef std::vector<int>::iterator vecIter;
typedef std::vector<int>::reverse_iterator vecRIter;
try
{
boost::any iter = getIterator(myvec, false);
boost::any iter2 = getIterator(myvec, true);
vecIter it1 = any_cast<vecIter>(iter);
vecRIter it2 = any_cast<vecRIter>(iter2);
MSG(*it1);//output 1
MSG(*it2);//output 3
return true;
}
catch(const boost::bad_any_cast &)
{
return false;
}
}
Use boost::variant or boost::any.
boost::variant< reverse_iterator, iterator >
generate_iterator(...) {
if(...) return iterator();
else return reverse_iterator();
}
// user code
boost::variant< reverse_iterator, iterator > v = generate_iterator();
if(reverse_iterator* it = boost::get<reverse_iterator>(v))
...;
else if(...)
...;
Although the variant is better accessed through a visitor.
The downside is that you need some boiler plate to extract the proper type and is exactly the reason why something like any_iterator might be a more sensible choice.
I have a C++ library (with over 50 source files) which uses a lot of STL routines with primary containers being list and vector. This has caused a huge code bloat and I would like to reduce the code bloat by creating another library which is essentially a wrapper
over the list and vector.
I basically need a wrapper around std::list which works perfectly for the list container of any type.
Shown below is my list wrapper class.
template<typename T>
class wlist
{
private:
std::list<T> m_list;
public:
wlist();
typedef std::list<void*>::iterator Iterator;
typedef std::list<void*>::const_iterator CIterator;
unsigned int size () { return m_list.size(); }
bool empty () { return m_list.empty(); }
void pop_back () { m_list.pop_back(); }
void pop_front () { m_list.pop_front(); }
void push_front (const T& item) { m_list.push_front(item); }
void push_back (const T& item) { m_list.push_back(item); }
bool delete_item (void* item);
T& back () { return (m_list.empty()) ? NULL : m_list.back();}
T& front () { return (m_list.empty()) ? NULL : m_list.front();}
Iterator erase() { return m_list.erase(); }
Iterator begin() { return (Iterator) m_list.begin(); }
Iterator end() { return (Iterator) m_list.end(); }
};
File1.h:
class label{
public:
int getPosition(void);
setPosition(int x);
private:
wlist<text> _elementText; // used in place of list<text> _elementText;
}
File2.h:
class image {
private:
void draw image() {
//Used instead of list<label*>::iterator currentElement = _elementText.begin();
wlist<label*>::iterator currentElement = _elementText.begin();
currentElement->getPosition(); // Here is the problem.
currentElement ++;
}
}
Invoking getPosition() bombs with the following error message:
error: request for member `getPosition' in `*(¤tElement)->std::_List_iterator<_Tp>::operator-> [with _Tp = void*]()', which is of non-class type `void*'
Type casting getPosition() to label type didn't work. Additionally my iterators are of type void*.
I think the problem is that the line
currentElement->getPosition();
won't work because currentElement is an iterator over void*s, not labels. Since iterators over some type T act like T*s, this means that your currentElement iterator acts like a label**, and so writing the above code is similar to writing
(*currentElement).getPosition();
Here, the problem should be a bit easier to see - *currentElement is a label*, not a label, and so you can't use the dot operator on it.
To fix this, trying changing this code to
((label *)(*currentElement))->getPosition();
This dereferences the iterator and typecasts the void* to get a label*, then uses the arrow operator to call the getPosition() function on the label being pointed at.
Your iterator types seem to be declared in terms of std::list<void*>::iterator. This doesn't sound right to me...