Is it safe to do a const cast here? - c++

I've written my own generic tree implementation, and when writing iterators for it, I am having trouble with const correctness. The issue I am having currently is as follows:
This is the header file for a DFS iterator I wrote:
template<class Item>
class DFSIterator
{
public:
DFSIterator(const Item& rRootNode);
~DFSIterator();
DFSIterator* First();
DFSIterator* operator++(int rhs);
Item* operator*() const;
Item* operator->() const;
bool isDone() const;
template <class Node> friend class Node;
private:
void initListIterator(const Item* currentNode);
bool m_bIsDone;
const Item* m_pRootNode;
const Item* m_pCurrentNode;
ListIterator<Item>* m_pCurrentListIter;
std::map<const Item*, ListIterator<Item>*> m_listMap;
};
So the bit I am concerned about is the dereference operator:
template<class Item>
Item* DFSIterator<Item>::operator*() const
{
if(isDone())
{
return NULL;
}
else
{
return const_cast<Item*>(m_pCurrentNode);
}
}
Is it appropriate to do a const_cast there? I'm wondering if this would cause problems if a user put const objects into the container?

As your constructor uses a const Item, your operator shoud return a const pointer.
If you want to return a non-const item, you should use a non const parameter for your constructor. A solution is having a base class using const objects, and a subclass working with non-const objects (A bit in a way it is done in Objc, eg NSString and NSMutableString).

Do not cast the const away, as it will break its meaning!
There is a reason why the STL has a const_iterator and iterator class. If you want const correctness you will have to implement two seperate iterator classes. One of the goals of a iterator is to mimic a save pointer. So you do not want to return null if you're beyond the iteration range, you want to raise a debug assertion if this actually happens.
A const iterator class could look something like this:
template<class Item>
class DFSIteratorConst
{
public:
DFSIteratorConst(const Item& node)
{
m_pCurrentNode = &node;
};
const Item& operator*() const
{
assert(!IsDone());
return *m_pCurrentNode;
}
void operator++()
{
assert(!IsDone());
m_pCurrentNode = m_pCurrentNode->next;
}
operator bool() const
{
return !IsDone();
}
bool IsDone() const
{
return m_pCurrentNode == nullptr;
}
private:
Item const * m_pCurrentNode;
};
A few things to note:
consider returning a reference to the node (a const reference). This causes you to not be able to return null if the iterator is past the last element. Dereferencing a past the end iterator is usually bad behaviour and you want to find those issues during debug builds and testing
the const iterator class has a pointer to a const element. Meaning that it can change the pointer (otherwise you would not be able to iterate) but can not modify the element itself
the implicit conversion to bool is practical if you want to check the iterator in a while loop. This allows you to write: while(it) { it++; } instead of while(!it.IsDone()) { it++; } again, something that resembles a classic pointer.
use nullptr if available
As I can see from your use of std::map your are already using the STL. Maybe it would be easier to use exsiting STL iterators?

const_casting in itself is always safe, but any attempt to write to const_casted values who have been declared const is undefined behaviour.
In this case, if your returned pointer points to a value that is declared const and you attempt to modify it, you'll get undefined behavior.
Rule of thumb: If you need to use const_cast for anything but interoperation with const-incorrect code, your design is broken.
The standard says in 7.1.6.1 The cv-qualifiers:
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

Normally, you write two iterators versions, an iterator and a const_iterator.
There are template tricks to avoid the code duplication that is exposed in the Boost.Iterator library documentation. See how the Iterator Adaptor is defined as it is quite long.
The thing is that you will write a BaseIterator that is const conscious, and then provide aliases:
typedef BaseIterator<Item> Iterator;
typedef BaseIterator<Item const> ConstIterator;
The trickery is in how you define the conversion constructor so that Iterator to ConstIterator is possible but the reverse is not.

The real problem is that this code/interface is "lying".
The constructor says: I don't change you (the root).
But the iterator gives changeable Item away.
To be "honest" either the constructor takes a changeable root, even when it will not changed within this class,
or this class does not give changeable Item away.
However, Item itself defines whether it give changeable childs away.
Depending on that code may not compile. Maybe this is the reason for your construction.
Long talking, short meaning: this design is poor and should be changed
Probably you need two templates depending on the "const"-ness of given childs away

Related

operator[] - differentiate between get and set?

Are there any advances in recent C++ that allows for differentiating between getting and setting values via the operator[] of a class? (as Python does via __setitem__ and __getitem__)
const T& operator[](unsigned int index) const;
T& operator[](unsigned int index);
I am wrapping an std::unordered_map, and want to let my users access the data via the operator[], but also do some behind the scenes record-keeping to keep things aligned in my data structure.
Searching reveals a few answers, but they are all many years old, and I was wondering if C++ has added extra functionality in the meantime.
Assume your wrapper class implements set and get methods that perform the appropriate record keeping actions. The wrapper class can then also implement operator[] to return a result object that will delegate to one of those methods depending on how the result is used.
This is in line with the first related question you identified (Operator[] C++ Get/Set).
A simple illustration is below. Note that a const map would not be able to call set_item anyway, so the const overload of operator[] calls get_item directly.
class MapType {
...
struct Result {
MapType &map_;
KeyType key_;
Result (MapType &m, KeyType k) : map_(m), key_(k) {}
operator const ValueType & () const {
return map_.get_item(key_);
}
ValueType & operator = (ValueType rhs) {
return map_.set_item(key_, rhs);
}
};
...
const ValueType & get_item (KeyType key) const {
/* ... record keeping ... */
return map_.at(key);
}
ValueType & set_item (KeyType key, ValueType v) {
/* ... record keeping ... */
return map_[key] = v;
}
...
Result operator [] (KeyType key) { return Result(*this, key); }
const ValueType & operator [] (KeyType key) const {
return get_item(key);
}
...
};
Actually there is not such a getting operator[] and setting operator[]. There are just constant and non-constant [] operators.
When objects are large, returning by reference may save an object creation, and also gives an opportunity to change the element on that position, so it makes sense to define these operators to return by reference than by value. To make the operator available on constant objects too, you should mark the constant overload with const keyword. The idea has not been changed since old times.
BTW, the STL has slightly different approaches with regard the container type. For example if you call [] with an out of range index in a vector, it will throw, but similar call on a non-constant map will create a node with given key. To keep things a little bit more consistent, STL provides a function named at() which regardless of container class, checks the index and throws if it is out of range.

Purpose of overloading member function returning iterator and const_iterator

In my class assignment I am suppose to implement member functions of a predefined class. I am confused on the purpose of these the overload on findStudent. Can this class not be designed to only contain one definition, which returns a regular iterator that can be casted as const? Or rather, use std::find since it is already overloaded to return a const_iterator?
class Class{
public:
Class(std::string name, std::string teacher, std::string capacity);
std::string getTeacher();
std::string getName();
int getCapacity();
void addStudent(std::string);
void removeStudent(std::string);
std::vector<std::string>::iterator findStudent();
std::vector<std::string>::const_iterator findStudent() const;
private:
std::string name;
std::string teacher;
int capacity;
std::vector<std::string> students;
};
The purpose of overloading those functions is that they differ by their const-ness.
Consider:
you pass Class foo; to a function
void bar(Class& foo).
If inside this function you were to call findStudent(), the non-const version of the member function would be invoked. As a result, you would get a mutating iterator of std::vector<std::string>. The iterator would allow to assign new values, clear strings and do whatever else you want with the values.
Now consider another case:
void bar(const Class& foo).
Inside this function, the const version of findStudents() will be called, and you won't be able to modify the values. You will be able to inspect them, to print them, to sum the lengths, i.e., you will be able to do only non-mutating operations on your students.
The purpose of this is to enable both the compiler and -- especially important! -- the programmer to reason about the code. Const functions do not change the state of the object, they conserve invariants of the code. For instance, it generally holds that if you call a const function twice in succession, both calls should return the same answer. (This holds "generally", but not always, especially if we are dealing with hardware).
When a member function is const-qualified, it can be called on a const object. (It can also be called on a non-const object, since a const conversion that ADDs const is a valid implicit conversion.)
Member functions that are not const qualified can only be called on non-const objects (because they can change the state of the object, defeating the meaning of const if such a conversion could happen.)
Thus:
class Foo {
public:
...
int& operator[](int idx) { return values[idx]; } // 1
int operator[](int idx) const { return values[idx]; } // 2
private:
int values[10];
};
We have two versions of operator[], one returning a mutable reference, and the other returning a copy (on which changes won't affect the object it came from.
And so
Foo f;
int& x = f[3]; // calls 1, since f is non-const
const Foo cf;
int y = cf[3]; // calls 2, since cf is const
If you want your code to work on const and non-const objects, but always return a const_iterator, then you could get by with only implementing a version of findStudent that has the const qualification, like op 2 does above, and not overload it with the non-const version.
I am guessing that findStudent is supposed to take a name (or other criteria) and return an iterator to that student, or an "end" iterator if it's not found. In that case, you'd use std::find() in the implementation of find_student. So be sure to allow callers to say what student they want to find!

How to make a deep-const pointer

Let's say I want to represent a binary tree in C++. Usually, I want a Node struct like this:
struct Node {
Node* left
Node* right;
};
(Here I use struct and raw pointers just for simplicity. I know I should use smart pointers for memory management.)
This representation has a problem: I can never have a deep-const tree. (Correct me if I can.) I may mark a single Node const, but its children is hard-coded as non-const in the Node struct.
(I may use some template hack to make left and right optionally const, but this makes the const Node and non-const Node incompatible.)
Soon I found out, if I magically had some deep-const pointer (say deep_const_pointer, which makes constness transitive), I can use that pointer in Node so that having a const node automatically means having a const sub-tree.
I tried to write a deep-const pointer class, and here is what I end up with:
template <typename T>
class deep_const_pointer {
public:
explicit deep_const_pointer(const T* p_)
: p{const_cast<T*>(p_)} {}
const T& operator*() const { return *p; }
T& operator*() { return *p; }
const T* operator->() const { return p; }
T* operator->() { return p; }
// etc.
private:
T* p;
};
Here I cast out the const in the constructor and optionally add it back according to the constness of this pointer-like object. However, this implementation allows the following:
const int i = 42;
deep_const_pointer<int> p{&i};
*p = 0; // Oops!
So it depends on the user to correctly mark whether the pointer is const or not.
How should I build a deep-const pointer class? Ideally, I want the const check happen at compile-time, and that pointer class takes as much memory as a raw pointer. (Which rules out the solution to save the constness to a bool member variable and check on each access.)
EDIT: I checked std::experimental::propagate_const, and it is indeed not a "deep-const" pointer from my perspective. What I meant by deep-const pointer P is:
Constant P is pointer-to-const;
Mutable P is pointer-to-mutable;
A const reference to a non-const P is treated as if it were a const P;
Since pointer-to-const has value semantics, a const P should be trivially copyable.
propagate_const fails to match the requirement because:
It never accepts a pointer-to-const;
It is not copyable (copy constructors explicitly deleted);
From the comments and answer I received, I guess such a P is not implementable in C++.
Writing a transitive-const smart pointer is a solved problem, just look up std::experimental::propagate_const<>. It shouldn't be too hard to find appropriate implementations.
In your own try, you got constructing from a raw pointer wrong. You should not add const to the pointee-type, nor strip it out with a cast.
Fixed:
explicit deep_const_pointer(T* p_)
: p{p_} {}

How to return a private pointer to a list of pointers as const?

I have a pointer to a list of pointers, as a private variable. I also have a getter that returns the pointer to the list. I need to protect it from changes.
I couldn't find how to use reinterpret_cast or const_cast on this.
class typeA{
shared_ptr<list<shared_ptr<typeB>>> l;
public:
shared_ptr<list<shared_ptr<const typeB>>> getList(){return (l);};
};
The compiler returns:
error: could not convert ‘((typeA*)this)->typeA::x’ from ‘std::shared_ptr<std::__cxx11::list<std::shared_ptr<typeB> > >’ to ‘std::shared_ptr<std::__cxx11::list<std::shared_ptr<const typeB> > >’|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
It seems as const shared_ptr<list<shared_ptr<typeB>>> and shared_ptr<const list<shared_ptr<typeB>>> work fine.
Is it possible to do return l as a complete const, like:
const shared_ptr<const list<shared_ptr<const typeB>>>
or at least like:
shared_ptr<list<shared_ptr<const typeB>>>
?
References instead of pointers is not an option. To declare l as shared_ptr<list<shared_ptr<const typeB>>> also is not a wanted solution.
EDIT: no 'int' anymore.
It seems as it is not possible exactly what I wanted, but the suggested solutions are good. Yes, copying pointers is acceptable.
My bad i didn't put typeB immediately. I am aware of some advantages of references over pointers, but I hoped there is some similar solution.
You can create a new list of const int's from your original list and return that:
std::shared_ptr<std::list<std::shared_ptr<const int>>> getList(){
return std::make_shared<std::list<std::shared_ptr<const int>>>(l->begin(), l->end());
}
If you want to prevent people from making changes to the returned list, make it const too:
std::shared_ptr<const std::list<std::shared_ptr<const T>>> getList(){
return std::make_shared<const std::list<std::shared_ptr<const T>>>(l->cbegin(), l->cend());
}
The shared pointer returned by this function does not point to the original list but to the newly created list.
An alternative may be to provide iterators that, when dereferenced, returns const T& (where T is the type you actually store). That way there will be no need to copy the whole list every time you want to go though it. Example:
#include <iostream>
#include <list>
#include <memory>
struct example {
int data;
example(int x) : data(x) {}
};
template <class T>
class typeA {
std::shared_ptr<std::list<std::shared_ptr<T>>> l = std::make_shared<std::list<std::shared_ptr<T>>>();
public:
template< class... Args >
void add( Args&&... args ) {
l->emplace_back(std::make_shared<T>(std::forward<Args>(args)...));
}
// a very basic iterator that can be extended as needed
struct const_iterator {
using uiterator = typename std::list<std::shared_ptr<T>>::const_iterator;
uiterator lit;
const_iterator(uiterator init) : lit(init) {}
const_iterator& operator++() { ++lit; return *this; }
const T& operator*() const { return *(*lit).get(); }
bool operator!=(const const_iterator& rhs) const { return lit != rhs.lit; }
};
const_iterator cbegin() const noexcept { return const_iterator(l->cbegin()); }
const_iterator cend() const noexcept { return const_iterator(l->cend()); }
auto begin() const noexcept { return cbegin(); }
auto end() const noexcept { return cend(); }
};
int main() {
typeA<example> apa;
apa.add(10);
apa.add(20);
apa.add(30);
for(auto& a : apa) {
// a.data = 5; // error: assignment of member ‘example::data’ in read-only object
std::cout << a.data << "\n";
}
}
When you convert a pointer-to-nonconst to a pointer-to-const, you have two pointers. Furthermore, a list of pointers-to-nonconst is a completely different type from a list of pointers-to-const.
Thus, if you want to return a pointer to a list of pointers-to-const, what you must have is a list of pointers-to-const. But you don't have such list. You have a list of pointers-to-nonconst and those list types are not interconvertible.
Of course, you could transform your pointers-to-nonconst into a list of pointers-to-const, but you must understand that it is a separate list. A pointer to former type cannot point to the latter.
So, here is an example to transform the list (I didn't test, may contain typos or mistakes):
list<shared_ptr<const int>> const_copy_of_list;
std::transform(l->begin(), l->end(), std::back_inserter(const_copy_of_list),
[](auto& ptr) {
return static_pointer_cast<const int>(ptr);
});
// or more simply as shown by Ted:
list<shared_ptr<const int>> const_copy_of_list(l->begin(), l->end());
Since we have created a completely new list, which cannot be pointed by l, it makes little sense to return a pointer. Let us return the list itself. The caller can wrap the list in shared ownership if the need it, but don't have to when it is against their needs:
list<shared_ptr<const int>> getConstCopyList() {
// ... the transorm above
return const_copy_of_list;
}
Note that while the list is separate, the pointers inside still point to the same integers.
As a side note, please consider whether shared ownership of an int object makes sense for your program - I'm assuming it is a simplification for the example.
Also reconsider whether "References instead of pointers is not an option" is a sensible requirement.
You problem squarely lies at
but I do not want to mix references and pointers. It is easier and cleaner to have just pointers.
What you are finding here is that statement is wrong. A list<TypeB> can bind a const list<TypeB> & reference, and none of the list's members will allow any modification of the TypeB objects.
class typeA {
std::vector<typeB> l;
public:
const std::vector<typeB> & getList() const { return l; };
};
If you really really must have const typeB, you could instead return a projection of l that has added const, but that wouldn't be a Container, but instead a Range (using the ranges library voted into C++20, see also its standalone implementation)
std::shared_ptr<const typeB> add_const(std::shared_ptr<typeB> ptr)
{
return { ptr, ptr.get() };
}
class typeA {
std::vector<std::shared_ptr<typeB>> l;
public:
auto getList() const { return l | std::ranges::transform(add_const); };
};
Another alternative is that you can wrap your std::shared_ptrs in something like std::experimental::propagate_const, and just directly return them.
What you have here is a VERY complex construct:
shared_ptr<list<shared_ptr<typeB>>> l;
This is three levels of indirection, of which two have reference counting lifetime management, and the third is a container (and not memory-contiguous at that).
Naturally, given this complex structure, it is not going to be easy to convert it to another type:
shared_ptr<list<shared_ptr<const typeB>>>
Notice that std::list<A> and std::list<const A> are two distinct types by design of standard library. When you want to pass around non-modifying handles to your containers, you are usually supposed to use const_iterators.
In your case there is a shared_ptr on top of the list, so you can't use iterators if you want that reference counting behavior.
At this point comes the question: do you REALLY want that behavior?
Are you expecting a situation where your typeA instance is destroyed, but you still have some other typeA instances with the same container?
Are you expecting a situation where all your typeA instances sharing the container are destroyed, but you still have some references to that container in other places of your runtime?
Are you expecting a situation where the container itself is destroyed, but you still have some references to some of the elements?
Do you have any reason at all to use std::list instead of more conventional containers to store shared pointers?
If you answer YES to all the bullet points, then to achieve your goal you'll probably have to design a new class that would behave as a holder for your shared_ptr<list<shared_ptr<typeB>>>, while only providing const access to the elements.
If, however, on one of the bullet points your answer is NO, consider redesigning the l type. I suggest starting with std::vector<typeB> and then only adding necessary modifications one by one.
The problem with templates is that for any
template <typename T>
class C { };
any two pairs C<TypeA> and C<TypeB> are totally unrelated classes – this is even the case if TypeA and TypeB only differ in const-ness.
So what you actually want to have is technically not possible. I won't present a new workaround for now, as there are already, but try to look a bit further: As denoted in comments already, you might be facing a XY problem.
Question is: What would a user do with such a list? She/he might be iterating over it – or access single elements. Then why not make your entire class look/behave like a list?
class typeA
{
// wondering pretty much why you need a shared pointer here at all!
// (instead of directly aggregating the list)
shared_ptr<list<shared_ptr<typeB>>> l;
public:
shared_ptr<list<shared_ptr<typeB>>>::const_iterator begin() { return l->begin(); }
shared_ptr<list<shared_ptr<typeB>>>::const_iterator end() { return l->end(); }
};
If you used a vector instead of a list, I'd yet provide an index operator:
shared_ptr<typeB /* const or not? */> operator[](size_t index);
Now one problem yet remains unsolved so far: The two const_iterators returned have an immutable shared pointer, but the pointee is still mutable!
This is a bit of trouble - you'll need to implement your own iterator class now:
class TypeA
{
public:
class iterator
{
std::list<std::shared_ptr<int>>::iterator i;
public:
// implementation as needed: operators, type traits, etc.
};
};
Have a look at std::iterator for a full example – be aware, though, that std::iterator is deprecated, so you'll need to implement the type-traits yourself.
The iterator tag to be used would be std::bidirectional_iterator_tag or random_access_iterator_tag (contiguous_iterator_tag with C++20), if you use a std::vector inside.
Now important is how you implement two of the needed operators:
std::shared_ptr<int const> TypeA::iterator::operator*()
{
return std::shared_ptr<int const>(*i);
}
std::shared_ptr<int const> TypeA::iterator::operator->()
{
return *this;
}
The other operators would just forward the operation to the internal iterators (increment, decrement if available, comparison, etc).
I do not claim this is the Holy Grail, the path you need to follow under all circumstances. But it is a valuable alternative worth to at least consider...

C++ Error with the operator* in a custom iterator for a b-tree

I'm getting the following error when trying to return the value using the operator*() in my custom iterator.
error: invalid initialization of non-const reference of type 'char&' from a temporary of type 'char'
If anyone is able to help me out, that would be great!
The specifics of the code is as follows:
Test code:
for(btree<char>::iterator itr = tree.begin(); itr != tree.end(); ++itr) {
cout << *itr << " "; // <- this line complains about the * operator.
}
The operator*() code:
template <typename T> typename btree_iterator<T>::reference btree_iterator<T>::operator*() const {
return pointee_->value(); // <- this line is where the error above is produced from.
}
pointee_ from the btree_iterator class is a private variable defined as follows:
btree_node<T>* pointee_;
reference is typefef'ed as the following in the btree_iterator class:
typedef T& reference;
value() is defined in the btree_node class as follows:
T value() const;
// function definition.
template <typename T> T btree_node<T>::value() const { return value_; }
value is initially stored into a btree_node via it's constructor as follows:
template <typename T> btree_node<T>::btree_node(const T& elem) : value_(elem), nextCont_(NULL), prevCont_(NULL), nextNode_(NULL), prevNode_(NULL) {}
Please let me know if i missed including any crucial information, and apologies if i did!
Based on the error, it sounds like I'm trying to return a reference to a local variable. The iterator itself may be local, but the value that it is returning shouldn't be, since the node constructor originally takes in a reference, and stores that into the node.
Thanks in advance, and regards!
You're trying to use a reference to a temporary variable
template <typename T> T btree_node<T>::value() const { return value_; }
^^^
This returns a temporary copy of value_.
template <typename T> typename btree_iterator<T>::reference btree_iterator<T>::operator*() const {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return pointee_->value(); // <- this line is where the error above is produced from.
}
This returns a reference to that temporary copy. You most likely want value() to return a reference, but this is up to you - a reference allows clients of your btree to alter the contents. Especially if the tree is ordered based upon the value of the node, you may not want to allow this. Have a think how this is relevant to your design.
pointee_->value() returns a values which is basically a fresh copy. Hence this value is local in operator* and you can't return a reference to that.
I'd recommend doing it just like most containers do, i.e. having both, iterator and const_iterator, whereas const_iterator returns a const reference and iterator returns a reference. The same principle applies to value(). Have versions returning a ref and a const ref.
If you ever want to work on copies, you can create them from the reference in client code. If you don't want to allow chanign the values, simply drop (or make private, or whatever) the non-const parts. It think it also fits what people using your btree expect:
Iterators do not make copies somewhere but you iterator over the data inside teh data structure. If you want copies, you can make them.