For fun and profitâ„¢, I'm writing a trie class in C++ (using the C++11 standard.)
My trie<T> has an iterator, trie<T>::iterator. (They're all actually functionally const_iterators, because you cannot modify a trie's value_type.) The iterator's class declaration looks partially like this:
template<typename T>
class trie<T>::iterator : public std::iterator<std::bidirectional_iterator_tag, T> {
friend class trie<T>;
struct state {
state(const trie<T>* const node, const typename std::vector<std::pair<typename T::value_type, std::unique_ptr<trie<T>>>>::const_iterator& node_map_it ) :
node{node}, node_map_it{node_map_it} {}
// This pointer is to const data:
const trie<T>* node;
typename std::vector<std::pair<typename T::value_type, std::unique_ptr<trie<T>>>>::const_iterator node_map_it;
};
public:
typedef const T value_type;
iterator() =default;
iterator(const trie<T>* node) {
parents.emplace(node, node->children.cbegin());
// ...
}
// ...
private:
std::stack<state> parents;
// ...
};
Notice that the node pointer is declared const. This is because (in my mind) the iterator should not be modifying the node that it points to; it is just an iterator.
Now, elsewhere in my main trie<T> class, I have an erase function that has a common STL signature--it takes an iterator to data to erase (and returns an iterator to the next object).
template<typename T>
typename trie<T>::iterator trie<T>::erase(const_iterator it)
{
// ...
// Cannot modify a const object!
it.parents.top().node->is_leaf = false;
// ...
}
The compiler complains because the node pointer is read-only! The erase function definitely should modify the trie that the iterator points to, even though the iterator shouldn't.
So, I have two questions:
Should iterator's constructors be public? trie<T> has the necessary begin() and end() members, and of course trie<T>::iterator and trie<T> are mutual friends, but I don't know what the convention is. Making them private would solve a lot of the angst I'm having about removing the const "promise" from the iterator's constructor.
What are the correct const semantics/conventions regarding the iterator and its node pointer here? Nobody has ever explained this to me, and I can't find any tutorials or articles on the Web. This is probably the more important question, but it does require a good deal of planning and proper implementation. I suppose it could be circumvented by just implementing 1, but it's the principle of the thing!
1) All iterators are required to be copy-constructible. Your iterator is Bi-directional, hence is also required to be default-constructible (http://en.cppreference.com/w/cpp/concept/ForwardIterator), although I don't know why. So the default constructor needs to be public but you can do what you like with the const trie<T>* one. I would think it should be private, since the purpose of this class is to provide the user with an iterator over the trie, and so its public interface should be only that of an iterator of the appropriate category. No need for any extra public constructors.
2) erase is a non-const function. The only iterators you can validly pass to it are iterators that refer to the same trie that the function is called on, which means (I think, although I'm not quite certain I've followed your design) the whole hierarchy of parents are non-const objects. So I suspect this is one of those cases where you can const_cast<trie<T>*>(it.parents.top().node). The iterator isn't allowed to use it to modify the trie, which is why you want it to hold a pointer-to-const. But when you hold a non-const pointer to the trie, namely this, you are allowed to modify any part of it you like, and the iterator is just giving you the position to start modifying from.
There might be some more general principle of const-safety you can draw here. One possible case in container::erase(const_iterator) functions is that the const container* you'd get from the iterator is equal to this. In that case the const_cast is certainly both safe and legitimate (as well as unnecessary, because you can just use this, but that's beside the point of whether it's const-correct or not). In your container it is not (in general) equal to this, it points to one of the several trie objects that together make up the hierarchical container that this is part of. The good news is, that whole container is logically const or logically non-const together, hence the const_cast is just as safe and legitimate as if it were all one object. But a bit harder to prove correct, because you have to make sure that in your design the whole hierarchical container genuinely does, as I've assumed, share non-constness.
Non-const methods of trie that wish to modify what a const_iterator points to (what it points to should be within this instance of trie) should just const_cast as needed. You know this is a safe and defined cast because if someone managed to call a non-const instance of this trie, then this instance of trie itself is not const.*
Alternatively you could do the opposite and hold non-const pointer(s) to the trie within the const_iterator, which will require a const_cast upon construction. This is safe for the same reason, above. The const_iterator methods will only provide const access, so the user can't mutate the part(s) of trie that the iterator points to. And if a const_iterator needs to be mutated in a non-const trie method it's okay because the trie must not have been const in the first place.*
The premise of the safety of const_casting here is that it is safe to hold and use a non-const pointer to a const object so long as you do not mutate that object. And it is safe to turn a cast away the constness of a pointer which points to something which wasn't originally declared as const.
*Yes, the caller of the non-const trie method might have const_casted in an undefined way; but in that case the burdon of causing undefined behavior is on their head, not tries.
Should iterator's constructors be public?
At least the copy constructor, yes. Have a look at this chart that describes the traits each type of iterator should have:
http://www.cplusplus.com/reference/iterator/
All iterator types should be copy-constructible, copy-assignable and destructible, so that means they need public copy constructors. Some iterators, for example the RandomAccessIterator should also be default constructible so the default constructor should be public as well.
What are the correct const semantics/conventions regarding the iterator and its node pointer here?
If you want to have an erase then you don't really have a const_iterator, you have a regular iterator. The difference between the two is that if you have a const trie object then you can only get a const_iterator out of it because you're not allowed to modify it in any way.
You can notice this in the STL containers. They tend to have both:
iterator begin();
const_iterator begin() const;
What usually happens is that you implement a const_iterator then:
class iterator : public const_iterator {...};
which implements the one or two non-const functions. This probably means just erase for you, since your operator* is going to stay const.
Related
Finding it difficult to wrap my head around why I can't have an immutable reference to a mutable object? If I pass a reference to a container like so:
auto f(const map<int, int>& x) -> decltype(non-const map iterator) {...}
It considers x to be const and any iterators I request of it are of the const_iterator type.
I want the following (or so I thought):
auto f(map<int, int>& const x) -> decltype(non-const map iterator)
But it does not compile.
This will not be hard to work around, but I was hoping for some uniformity in my project's code-base regarding const.
Long story short, you can't, in any reliable way, get an iterator out of a const std container.
To illustrate why that is, take a look at how containers are implemented.
template<typename T>
class map
{
struct node {
T data;
node *lchild, *rchild, *parent;
};
public:
iterator begin();
const_iterator begin() const;
private:
node* head;
};
Containers, due to their nature, needs to treat constness in an indirect way. They typically hold a pointer to memory that holds the objects, qualifying a container with const only affects the pointer and its methods.
// const map looks like
node* const head;
If containers were to naively return an iterator even when it is qualified with const, the compiler won't complain, you didn't syntactically mutate anything in the container after all.
// this is legal but wrong in a const map
head->data = T{};
// constness only prevents this
head = nullptr;
However since a container is conceptually the pointer and the stuff pointed to by the pointer, returning a iterator when qualified with const is semantically wrong. To remedy that, we introduce the const overloads, guaranteeing that a const container will truly be const.
As far as the map is concerned, giving out an iterator instantly breaks the contract that const should afford. There is no way the map can ensure its constness after it gives out an iterator, you may or may not mutate the contents with it.
My problem is the use of const_cast<>. I have a class which inherits from vector. Now in one of its member functions when I use this->begin(), this being a const pointer, it returns a constant iterator but I want to obtain a non-const iterator. The code looks something like ...
class xyz : private std::vector<//something>
{
public:
xyz();
void doSomething();
}
void doSomething()
{
xyz::iterator it;
it = this->begin();
*it.insert(something); //here is the problem and this where I need to use const_cast
// and in a lot more places
}
In the above function, since this->begin() returns a const iterator, I am forced to use a constant iterator and do a typecasting whenever I need to insert an element.
I thought of using const_cast at this->begin() but my friend told me that it is a bad idea to remove the constness of the this pointer. If it is so, then what is the way around?
If the vector is part of the state of the class, either as a member or
as a base (although it's really bad practice to publicly inherit from
vector), then a const member function should not modify it. If the
vector is part of the state, and a function modifies it, the function
should not be const.
Generally. There are special cases where, for example, the vector
represents cached state, that is logically calculated, but whose
calculation is expensive. In such cases, you can make the vector a
mutable member. But do be sure that the vector really doesn't represent
"observable state".
And finally, the code you post doesn't seem to have anything to do with
the problem. For starters, the only function is non-const. And the
code won't compile, for several reasons (use of this in a non-member
function, invocation of a member function insert on an iterator, which
doesn't have such a member, etc.).
I have a privately scoped Boost.BiMap in a class, and I would like to export a public view of part of this map. I have two questions about the following code:
class Object {
typedef bimap<
unordered_set_of<Point>,
unordered_multiset_of<Value>
> PointMap;
PointMap point_map;
public:
??? GetPoints(Value v) {
...
}
The first question is if my method of iteration to get the Point's associated with a Value is correct. Below is the code I'm using to iterate over the points. My question is if I am iterating correctly because I found that I had to include the it->first == value condition, and wasn't sure if this was required given a better interface that I may not know about.
PointMap::right_const_iterator it;
it = point_map.right.find(value);
while (it != point_map.right.end() && it->first == val) {
/* do stuff */
}
The second question is what is the best way to provide a public view of the GetPoints (the ??? return type above) without exposing the bimap iterator because it seems that the caller would have to know about point_map.right.end(). Any efficient structure such as a list of references or a set would work, but I'm a bit lost on how to create the collection.
Thanks!
The first question:
Since you are using the unordered_multiset_of collection type for the right side of your bimap type, it means that it will have an interface compatible with std::unordered_multimap. std::unordered_multimap has the member function equal_range(const Key& key) which returns a std::pair of iterators, one pointing to the first element that has the desired key and one that points to one past the end of the range of elements that have the same key. Using that you can iterate over the range with the matching key without comparing the key to the value in the iteration condition.
See http://www.boost.org/doc/libs/1_41_0/libs/bimap/doc/html/boost_bimap/the_tutorial/controlling_collection_types.html and http://en.cppreference.com/w/cpp/container/unordered_multimap/equal_range for references.
The second question:
Constructing a list or other actual container of pointers or references to the elements with the matching values and returning that is inefficient since it's always going to require O(n) space, whereas just letting the user iterate over the range in the original bimap only requires returning two iterators, which only require O(1) memory.
You can either write a member function that returns the iterators directly, e.g.
typedef PointMap::right_const_iterator match_iterator;
std::pair<match_iterator, match_iterator> GetPoints(Value v) {
return point_map.right.equal_range(v);
}
or you can write a proxy class that presents a container-like interface by having begin() and end() member functions returning those two iterators, and have your GetPoints() member function return an object of that type:
class MatchList {
typedef PointMap::right_const_iterator iterator;
std::pair<iterator, iterator> m_iters;
public:
MatchList(std::pair<iterator, iterator> const& p) : m_iters(p) {}
MatchList(MatchList const&) = delete;
MatchList(MatchList&&) = delete;
MatchList& operator=(MatchList const&) = delete;
iterator begin() { return m_iters.first; }
iterator end() { return m_iters.second; }
};
It's a good idea to make it uncopyable, unmovable and unassignable (like I've done above by deleting the relevant member functions) since the user may otherwise keep a copy of the proxy class and try to access it later when the iterators could be invalidated.
The first way means writing less code, the second means presenting a more common interface to the user (and allows for hiding more stuff in the proxy class if you need to modify the implementation later).
I'm a little confuse about meaning of this const keyword
I have a class like this
class ClassA {
public:
typedef std::tr1::shared_ptr<ClassA> ptr;
typedef std::tr1::shared_ptr<const ClassA> const_ptr;
void oper() const;
void oper();
private:
.....
};
int main()
{
std::list<ClassA::const_ptr> const_list;
.....
for(std::list<ClassA::const_ptr>::iterator it = const_list.begin();\
it != const_list.end(); it++)
{
(*it)->oper();
}
return 0;
}
I already get const version of oper() from the code above. So I can't imagine what will I get if I change std::list::iterator to std::list::const_iterator.
Your situation is a bit confusing because there are two levels of indirection (the iterator and the smart pointer), with const being applicable in some way to any of them (and also to the referenced object).
You can apply const:
to the object itself; this means that it cannot be modified;
to the smart pointer; this means that the smart pointer cannot be modified, e.g. cannot be reseated via reset;
in some sense to the iterator, using a const_iterator; this means that it will yield a const reference to the object it refers (=>the smart pointer) and that it cannot be used to modify the sequence it refers to.
Expanding a little:
Remember that a const shared_ptr<const ClassA>& (which is what you get by dereferencing a const_iterator) is different from a shared_ptr<const ClassA>& (which you get from a normal iterator): although on both you cannot modify the pointed object (due to the fact that shared_ptr refers to a const ClassA), on the const one you cannot modify the shared_ptr itself, which e.g. means that you can't reset it to point to another object, you cannot assign another shared_ptr to it, ...
Remember also that const versions of iterators, beside yielding a const reference to what they refer to, also disallow modifying the container through them (e.g. you cannot erase an element via a const_iterator).
Not sure if you understand what void oper() const in class ClassA means: In particular, it means that means is that ClassA::oper() is not allowed to modify any members of ClassA.
Which has little baring on your choice of iterator or const_iterator, that choice would have different implications.
I'm trying to understand what const_iterator means. I have the following example code:
void CustomerService::RefreshCustomers()
{
for(std::vector<Customer*>::const_iterator it = customers_.begin();
it != customers_.end() ; it ++)
{
(*it)->Refresh();
}
}
Refresh() is a method in the Customer class and it is not defined as const. At first thought I thought const_iterator was supposed to disallow modification to the elements of the container. However, this code compiles without complaint. Is this because there's an extra level of indirection going on? What exactly does const_iterator do/mean?
UPDATE
And in a situation like this, is it best practice to use const_iterator?
A const_iterator over a vector<Customer*> will give you a Customer * const not a Customer const*. So you cannot actually change the value being iterated (a pointer), but you sure can change the object pointed to by it. Basically all it says in your case is that you can't do this:
*it = ..something..;
You're not modifying the contents of the container. The contents of the container are just pointers. However, you can freely modify whatever the pointers point to.
If you didn't want to be able to modify what the pointers point to, you'd need a vector<const Customer*>.
const_iterator is not about whether you can modify the container or not, but about whether you can modify the objects in the container or not. In your case the container contains pointers, and you cannot modify the pointers themselves (any more than you could modify integers...) You can still make call to non-const Refresh() behind a pointer from the collection, because that call does not change the pointer itself.
Difference between const_iterator and iterator is important [only] when your container contains e.g. class instances, not pointers to them, but the instances themselves, for example in a container
list < pair < int , int > >
If 'it' is a const_iterator into this list, you can't do
it->first = 5
but if it is iterator (not const_iterator), that works.