I'm developing a class that must return an iterator with the begin() method. Also, I have to develop a function that receives a const reference of this class and iterates over it.
When I try to get an iterator from this method, I have the following compilation error: "the object has type qualifiers that are not compatible with the member function."I can't understand why this error appears.
Here is the code that I have written:
// ------------ class Neuron -------------
class Neuron { ... };
// ---------------------------------
// ------------ class AbstractLayer -------------
class AbstractLayer {
public:
class Iterator : public std::iterator<std::input_iterator_tag, Neuron> {
public:
Iterator(Neuron *neurons) : _neurons(neurons) {}
private:
Neuron *_neurons;
};
virtual Iterator begin() = 0;
virtual const Iterator begin2() = 0;
};
// ----------------------------------------
// ------------ class Layer -------------
class Layer : AbstractLayer {
public:
Layer(){};
Iterator begin(){ return Iterator(_neurons); }
const Iterator begin2(){ return (const Iterator)begin(); }
private:
Neuron *_neurons;
int _size;
};
// --------------------------------
// ------------ Method where the problem is -------------------
void method(const AbstractLayer &layer){
// Error in both methods:
// "the object has type qualifiers that are not compatible with the member function."
layer.begin();
layer.begin2();
}
// -------------------------------------------------------------
In the method function, layer references a constant object. That means you can only call functions marked as const. Like e.g.
class AbstractLayer {
public:
...
virtual const Iterator begin() const = 0; // <- Note use of `const` here
...
};
You do not need begin and begin2. What you need is two versions of begin - const and non-const. A const one would return const iterator. You also might want a cbegin (const only) which would always return const iterator.
In method, the layer argument is const which prevents you from calling non-const methods on it. If you take layer by non-const reference (void method(AbstractLayer &layer)), you will be able to call both methods.
You should probably provide a const begin method that returns a const_iterator so you can iterate over a const AbstractLayer.
Your function accepts a const AbstractLayer which means that only const member functions can be called on it. However, begin and begin2 are not const. In fact, given that only begin2 returns a const Iterator, it wouldn't make sense to try and call begin anyway in this method.
Change
virtual const Iterator begin2() = 0;
to
virtual const Iterator begin2() const = 0;
and
const Iterator begin2()
to
const Iterator begin2() const
Finally, returning a const Iterator is actually meaningless in your code, as the const is discarded on account of an rvalue being returned. Regardless, you shouldn't need to perform casting to const Iterator when you call begin; just return an Iterator and the compiler will take care to make it const.
Finally, your Layer class needs to derive publicly from AbstractLayer:
class Layer : public AbstractLayer
Live Demo
Related
What I want to achieve is probably easily explained: Consider I have an abstract class that I know will contain multiple objects of known type. However the actual container holding these objects will be implemented in sub-classes.
In my abstract base class I now want to provide an interface to iterate over these objects. Given that I don't know (or rather don't want to fix) the type of container, I thought that iterators would probably be my best bet.
A conceptual declaration of this class might look like this:
class MyClass {
public:
// Other interface methods, e.g. size()
virtual Iterable<MyObject> objects() = 0;
};
The intention here is that I'll be able to iterate over the nested objects of my class like this:
MyClass *class = new ImplementationOfClass();
for (const MyObject &obj : class->objects()) {
// Do stuff with obj
}
The issue I am facing however is that I can't seem to figure out how Iterable<MyObject> should be defined. The key property of this object is that at the time of defining this class I can only specify that the returned value will be iterable (using STL-style iterators) and will yield objects of type MyObject when the used iterator is dereferenced.
Normally I would use an abstract class on its own for this but it seems that this is very tricky (impossible?) since iterators are always passed by value and thus to my knowledge no Polymorphism is possible.
Questions dealing with how to pass arbitrary iterator types as arguments into a function always come up with the "use templates" answer. However I think in my case I can't use templates for that. This assumption might be wrong though, so feel free to correct me.
Essentially the barrier I always run into is that at some point I have to write down the iterator type explicitly which in my case I can't. I thought about using a template for that but this would then inhibit proper Polymorphism (I think?) because the user of that abstract interface seems to have the burden of explicitly initializing the correct template. The whole point of all of this however is that the caller does not have to care about the underlying structure.
TL;DR: Is there a way to create an interface class that only promises to be iterable and that dereferencing an iterator will yield an object of type T?
With the help of #FrançoisAndrieux and a hint from https://stackoverflow.com/a/4247445/3907364, I was able to come up with an approach to my problem.
Essentially the idea is to create an iterator-wrapper that stores a function to obtain an object of the given type if given an index. That index is then what is iterated on.
The nice thing about this is that the iterator interface is fixed by specifying the type of object that dereferencing it should return. The polymorphism comes into play by making the member function objects() virtual so that each sub-class can construct the iterator itself, providing a custom function pointer. Thus as long as there is a way to map an index to the respective element in the container (whichever is used), this trick is usable.
Note that you can either directly use pointers to e.g.std::vector<T>::at or create a custom function that will return the respective element.
Here's the implementation for the iterator (The implementation could probably be improved upon but it seems to get the job done):
template< typename T > struct iterator_impl {
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T *;
using reference = T &;
using access_function_t = std::function< T &(std::size_t) >;
// regular Ctor
iterator_impl(std::size_t start, access_function_t &func, const void *id)
: m_index(start), m_func(func), m_id(id) {}
// function-move Ctor
iterator_impl(std::size_t start, access_function_t &&func, const void *id)
: m_index(start), m_func(func), m_id(id) {}
// copy Ctor
iterator_impl(const iterator_impl &) = default;
// move ctor
iterator_impl(iterator_impl &&other) {
std::swap(m_index, other.m_index);
m_func = std::move(other.m_func);
std::swap(m_id, other.m_id);
}
// copy-assignment
iterator_impl &operator=(const iterator_impl &other) = default;
// prefix-increment
iterator_impl &operator++() {
++m_index;
return *this;
}
// postfix-increment
iterator_impl operator++(int) {
iterator_impl old = *this;
++(*this);
return old;
}
bool operator==(const iterator_impl &other) { return m_index == other.m_index && m_id == other.m_id; }
bool operator!=(const iterator_impl &other) { return !(*this == other); }
T &operator*() { return m_func(m_index); }
T *operator->() { return &m_func(m_index); };
protected:
std::size_t m_index = 0;
access_function_t m_func;
const void *m_id = nullptr;
};
Note that I had to introduce the m_id member variable as a means to properly compare iterators (std::function can't be compared using ==). it is meant to be e.g. the address of the container the elements are contained in. Its sole purpose is to make sure that 2 iterators that happen to have the same index but are iterating over completely different sets are not considered equal.
And based on that here's an implementation of an Iterable<T>:
template< typename T > struct Iterable {
using iterator = iterator_impl< T >;
using const_iterator = iterator_impl< const std::remove_const_t< T > >;
Iterable(std::size_t start, std::size_t end, typename iterator_impl< T >::access_function_t &func, const void *id)
: m_begin(start, func, id), m_end(end, func, id) {}
iterator begin() { return m_begin; }
iterator end() { return m_end; }
const_iterator begin() const { return m_begin; }
const_iterator end() const { return m_end; }
const_iterator cbegin() const { return m_begin; }
const_iterator cend() const { return m_end; }
protected:
iterator m_begin;
iterator m_end;
};
I've implemented a list and iterator templates, the find method is supposed to receive a functor so I declared and implemented one but I keep getting the error that there's no such an object!
"no matching function for call to object of type const findBond "
here's the implementation of the find method:
template <class T>
template <class Predicate>
Iterator<T> List<T> :: find(const Predicate &predicate) {
for (Iterator<T> iterator=begin(); iterator != end(); ++iterator) {
if (predicate(*iterator)) {
return iterator;
}
}
return end();
}
// predicate is a functor that is supposed to return a boolean value
here's the function object:
class findBond{
Bond& bond;
public:
findBond( Bond& bond1) : bond(bond1) {}
bool operator() (Bond& bond1){
return bond==bond1;
}
};
I'm trying to use them here:
void InvestmentBroker :: addBond(const string& name, double value, int amount ){
Bond bond = *new Bond(name, value, amount);
if (bondsDatabase.find(findBond(bond)) != bondsDatabase.end()) {
//throw an exception
} else {
// insert bond to dataBase
}
}
I included the needed files so it's not about the includes
What's wrong? What am I missing here?
Your find method takes a const Predicate& as its argument. That means you can only call const methods of the predicate. However, your functor's call operator is not declared const. You can fix your problem by declaring it const like this:
bool operator() (Bond& bond1) const {/* do stuff */ }
That const at the end of the declaration means that you can't modify this from within the function, which in turn means you can call the function on a const object.
It looks like a constness issue to me - findBond should be const on most arguments.
Change findBond to this
class findBond{
const Bond& bond;
public:
findBond( const Bond& bond1) : bond(bond1) {}
bool operator() const ( const Bond& bond1){
return bond==bond1;
}
};
I wish to define an iterator class over a vector, and how can its private member p match the return type of std::vector::begin()?
class A{
struct element{
...
}
class e_iterator {
e_iterator() : p()
...
private:
element* p;
};
e_iterator e_begin() const{
e_iterator Iter;
Iter.p = e_.begin(); // error
return Iter;
}
std::vector<element> e_;
}
I receive an error with element* p:
error: cannot convert 'std::vector<element, std::allocator<element>>::const_iterator' to 'element*' in assignment
With what you've given, the most I can suggest is changing p to:
std::vector<element>::const_iterator p;
The simple fact is that a std::vector iterator is not a pointer (probably). It is an unspecified type that behaves like a pointer - it meets the requirements of a Random Access Iterator.
When you call begin() on a non-const container, you get an iterator type. When you call it on a const iterator, you get a const_iterator type. Since your a_begin member function is marked const and e_ appears to be a std::vector<element> member of your class, e_ is transitively const, so calling begin() on it will also get you a const_iterator.
But it's quite hard to tell exactly what you're trying to do here and whether this is the right way to go about it.
Either change element* p to const element* p in your iterator class or remove the const qualifier from your e_begin() method.
Or provide both const/non-const iterator. I also suggest to initialize the pointer in the iterator constructor:
template <bool isconst>
class e_iterator_ {
public:
typedef std::conditional<isconst, const element*, element*>::type elementptr;
e_iterator_(elementptr e) : p(e)
...
private:
elementptr p;
};
typedef e_iterator_<true> const_e_iterator;
typedef e_iterator_<false> e_iterator;
Then you could use the data() member method of std::vector to directly access the underlying array of the vector.
e_iterator e_begin() const{
const_e_iterator Iter(e_.data());
return Iter;
}
Hope this helps.
I am trying to pass a STL container in a class to another class
For ex.
typedef std::map<std::string, int*> Container_t;
class A
{
public:
const Container_t * get_container() const;
private:
Container_t container;
};
const Container_t * A::get_container() const
{
return &container;
}
And when I try to get this in another class, I get compilation error
saying
error: argument of type ‘Container_t* (A::)()const’ does not match ‘Container_t*’
void foo(A * a)
{
const Container_t * container = a->get_container();
}
It will be best if I can get const reference instead of pointer. but I don't want to copy the return value of function in A. So it has to be either pointer or reference. Thank you
You need to change your function signature to:
const Container_t * get_container() const;
The reason is that the last const there at the end says "calling this function and using what it returns will not change this object." However, returning a (non-const) pointer to the member variable gives the caller freedom to change that variable, so you have to return a const Container_t * to ensure that the caller doesn't modify the member variable (and thereby modify the object).
I'm trying to understand better how does const-correctness work and more specifically, when dealing with classes whose members are based on containers and smart pointers.
I guess that the const-correctness property is the same regardless of the class members. However, since I'm having some difficulties to clearly understand what's going on,
I decided to ask you for advice.
So, here is the context. I've a ShapeContainer class that has as private class member a vector of smart pointers.
The Shape class is abstract and has the following virtual function virtual float doSomething(); which is then redefined by its derived classes. Note that it's a non-const class function.
The relevant part of the code is given below:
class ShapeContainer{
public:
typedef std::shared_ptr<Shape> ShapePtr;
typedef std::vector<ShapePtr> ShapePtrContainer;
// .......
const ShapePtr & operator[]( int ) const { return m_vect[index]; }; // const version
// ShapePtr & operator[]( int ) { return m_vect[index]; }; // non-const version
// .......
private:
ShapePtrContainer m_vect;
};
class Shape{
public:
// ...
virtual float doSomething() = 0;
};
Here are my questions.
Q1. Why do I'm allowed to call the doSomething() function in the following way: int index = 0; float tmp = container1[index]->doSomething(); (having ShapeContainer container1=createBasicShapes();)?
From what I understand, after calling to the const ShapePtr operator[] const function we'll get a const pointer to a Shape object, however the doSomething() virtual
function is not const. So, how does a reference to a const-object can call a non-const function?
Q2. By calling the doSomething() function as previouly ilustrated (float tmp =container1[index]->doSomething();) and by adding a non-const version of the operator[], this latter
overloaded version is then called instead of the const-version one. Why does it is so?
Now, instead of having a ShapeContainer class, I've now a new class named ShapeContainerInfo that still has a vector but of an intermediate ShapeInfo class (that has a smart pointer as a class member).
class ShapeContainerInfo{
public:
typedef std::vector<ShapeInfo> ShapeContainer;
const ShapeInfo & operator []( int index) const { return m_vect[index]; };
// ShapeInfo & operator []( int index) { return m_vect[index]; }; // non-const version
private:
ShapeContainer m_vect;
};
class ShapeInfo{
public:
typedef std::shared_ptr<Shape> ShapePtr;
// ...
float doSomething(){ return m_ShapePtr->doSomething(); };
private:
ShapePtr m_ShapePtr;
int m_nID;
};
Q3. When I call float tmp = container2[i].doSomething();, I get the following compiler error: error C2662: 'ShapeInfo::doSomething' : cannot convert 'this' pointer from 'const ShapeInfo' to 'ShapeInfo &'.
However, when I add a non-const vesion of the overloaded operator [] the compiler error is gone. So, why do I really need the non-const operator[] for ShapeContainerInfo and not for ShapeContainer?
Q4. If the m_vect private member of ShapeContainerInfo is set now as public member and only the const-version of operator[] is defined (not the non-const one), there are no compiler error messages. Why this? e.g. after setting m_vect to be a public class member: float tmp = info.m_vect[i].doSomething();
Q5. How could I correctly define both the ShapeInfo and ShapeContainerInfo classes such that I only need to define the const-version of the operator[] and still being able to call the float doSomething() function?
For those of you interested in the whole sample code, please find it here.
Clarifications, suggestions are always welcomed :-)
Merci!
Q1: The shared_ptr is const, that doesn't mean that the pointed to object is const. For that you would want shared_ptr<const Shape>.
Q2: Since you're ShapeContainer was not const, the non-const function was a better match, so it was called instead of the const version.
Q3: vector propagates its constness to its elements. shared_ptr does not. This is inline with the behavior of arrays and raw pointers. The elements of const arrays are const. The thing pointed to by a const pointer is not (necessarily) const.
Q4: Are you saying this produces no error?
ShapeContainerInfo info;
info[0].doSomething();
Please clarify, because that should be an error.
Q4: Okay, so you're saying that this produces no error:
ShapeContainerInfo info;
info.m_vect[0].doSomething();
Nor should it. The vector is not const. It's only inside the const member function that the vector(and all other members) are treated as const.
Q5: Make m_vect a vector of unique pointers. Inside the const function, the vector itself will be const, and the unique pointers will be const. But the objects that the unique pointers point to will be mutable.
As an example, the set function in this class is not legal:
struct Foo
{
void set(int index, int value) const
{
v[index] = value;
}
std::vector<int> v;
};
But this one is:
struct Foo
{
void set(int index, int value) const
{
*v[index] = value;
}
std::vector<std::unique_ptr<int>> v;
};