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_} {}
Related
template <class T>
class deep_const_ptr
{
T * priv;
public:
deep_const_ptr(const deep_const_ptr & p_other); // copy ctor
T const * operator->() const;
T * operator->();
..... // other crap
};
void Cheese::doThings(const deep_const_ptr<Cheese> p_other)
{
deep_const_ptr<Cheese> awayGoesTheConst(p_other); // I don't want this to work.
awayGoesTheConst->doTerribleThings();
}
I'd like to block copy construction from a const to a non-const.
Maybe this doesn't even make any sense?
Might this be possible with the new C++11 / 14 / 17?
EDIT:
Yes, the issue is that if I don't give the copy constructor, STL containers wont work. And I'd quite need them to work.
EDIT:
deep_const_ptr was the solution what I came up with when faced with the problem of STL containers and const objects.
class Shape
{
private:
std::list<Vertex *> m_vertices;
public:
virtual void std::list<Vertex*> * const getVertices() { return &m_vertices; }
virtual void std::list<Vertex const *> * const getVertices() const { return &m_vertices; } // doesn't work, cannot convert between different types of lists
}
I want to provide Immutable Vertex objects from an Immutable Shape.
The two solutions I came up with were custom deep_const_ptr pointer types and custom lists where
Vertex const *& operator[](unsigned int) const;
I assume what you are after is a deep-const-pointer implementation and what to do with its copying. As you already found out, a simple copy constructor will not do, since you will just lose the const on your pointer. There is no other way to disallow the const removal on the outer object, so you just need to disable the copy constructor.
Now the current version of your question does not make it clear why that is a problem. From your comments and the previous question, I can only infer that you want to use the pointer in containers, std::vector<> specifically. The key to making that work is to disable copy, but enable move (Note that you need rvalue-reference support for this, which is part of C++11):
template <class T>
class deep_const_ptr
{
T * priv;
public:
deep_const_ptr(const deep_const_ptr &) = delete; // Do not allow this!
deep_const_ptr& operator=(const deep_const_ptr &) = delete; // Nor this!
deep_const_ptr(deep_const_ptr &&) = default; // But this is okay
deep_const_ptr& operator=(deep_const_ptr &&) = default; // So is this!
T const * operator->() const;
T * operator->();
..... // other crap
};
Containers typically only need move, not copy, to work.
If you really need support for the copy, you need to do it to a different type that only supports the const access. For example, you could specialize the deep_const_ptr for const T and only implement the const variant of operator-> and operator* in there. Additionally, you'd allow "demotion" of a deep_const_ptr to a deep_const_ptr via the conversion operator. But this implies some sort of multiple owner handling, such as reference counting, so I'm going to leave it out. It's also weird that the const and non-const version behave differently, but it's your decision. Personally, I'd recommend just sticking with the move.
Edit Edit: You updated your answer again, so let me reply to that.
Just use a const pointer:
void Cheese::doThings(const Cheese* p_other)
{
Cheese* p = p_other; // compiler ERROR
p->doTerribleThings(); // can not do terrible things
}
Or if you want a shared_ptr:
void Cheese::doThings(const std::shared_ptr<const Cheese>& p_other)
{
std::shared_ptr<Cheese> p = p_other; // compiler ERROR
p->doTerribleThings(); // can not do terrible things
}
const ptr<T> is not the same as ptr<const T>. The first indicates a pointer object which can not be changed, while the object pointed to can. The second indicates a pointer object which can be changed, but the object pointed to can not.
For a function signature void foo(const A a) does not make much sense. You say "give me a copy, and I promise to not change it". Who cares what you do with your copy? Use void foo(const A& a) to avoid creating a copy.
Edit:
I'd like to allow copying nonconst -> nonconst, nonconst -> const,
const -> const.
This is already given for a "normal" pointer T* or an std::shared_ptr.
Old:
Just use T* operator->() const;. If the stored pointer will be a non-const pointer like Cheese* it can be converted to a const Cheese* automatically.
The function operator->() is marked as const, as it does not change the pointer object itself. This has nothing to do with the stored object!
Example with vector:
std::vector<Cheese*> a(10);
a[0] = 0; // does change a
Cheese* p = a[0]; // does not change a
a[0]->foo(); // still does not change a!
If you want objects pointed to by deep_const_ptr to be const, you have to use deep_const_ptr<const Cheese>.
Example with vector:
std::vector<const Cheese*> a(10); // const pointer!
a[0] = 0; // does change a
const Cheese* p = a[0]; // does not change a
a[0]->foo(); // now this only works if Cheese::foo is const
I am writing a class where multiple instances are linked together like a linked list. Here's a barebones example of the relevant code:
class A
{
public:
void setNext(const A& node) { next = &node; }
const A& getNext() const { return *next; }
private:
const A* next;
}
I have declared the argument to setNext as const because node is not modified by setNext. Class A does not modify the next node either, so the member variable next is declared const. The problem occurs with getNext. Whatever object obtains the next node from the current node may very well need to modify the node, but since the member variable is const the returned reference must be const as well.
From what I've read, const_cast usually indicates a poor design, but it seems like I would have to use it either inside the object that obtains the next node or use it within Class A to return a non-const reference. Is this a valid use of const_cast or is there a flaw in my design somewhere?
And while we're on the subject, is there a preference one way or the other on whether to return a reference or a pointer? I have setNext take a reference to ensure I get a valid instance of A, but the returned value could be returned either way.
I think linked-list private members must be non-const, cause you may want to implement a method which can go across the list an call a specific function on each member, for example. So, your design about a const next member can have a lot of limits. Other thing, you must take care receiving a reference on your setNext : how can you know the end of your list? With a pointer you can set your next as NULL and it'll be the end of the list. But you can keep your member non-const and implement a getNext which return a const object, in this case you'll have :
class A
{
public:
void setNext(A* node) { next = node; }
A& getNext() const { return *next; }
const A& getNext() const { return *next; }
private:
A* next;
}
First, don't reinvent the wheel. If at all possible use boost::intrusive which has already implemented an intrusive list such as this for you, with no additional work needed! You might even be able to get away with std::list or another standard container.
But if you have to:
The pointer to the next variable can't be const because you may need to modify the next node it points to.
setNext will need to take its parameter by non-const reference, because you need to be able to store a non-const item pointer.
Create two overloads of getNext: const A& getNext() const { return *next; } and A& getNext() { return *next; }
I am reading through implementing smart pointers and I found the following code,
template <class T>
class SmartPtr
{
public:
explicit SmartPtr(T* pointee) : pointee_(pointee);
SmartPtr& operator=(const SmartPtr& other);
~SmartPtr();
T& operator*() const
{
...
return *pointee_;
}
T* operator->() const
{
...
return pointee_;
}
private:
T* pointee_;
...
};
I am not able to understand the following,
"SmartPtr& operator=(const SmartPtr& other)": why the parameter is constant? Doesn't it lose its ownership when the assignment is done?
And why do we need "T& operator*() const" and "T* operator->() const" methods?
Thx#
Point 1. Not necessarily, depends on the design of the smart pointer. Some like boost:shared_ptr do not transfer ownership on assignment.
Point 2. Those methods simulate normal pointer operations on the smart pointer.
To answer 2.:
To simulate a raw pointer. You can use *ptr to return the object it points to (just like a C-pointer), and you can use ptr->foo() to call the method foo in T, (just like a C-pointer).
There are two types of smart pointer semantics I can think of that would work with that signature. The first would be a reference counting pointer like std::shared_ptr. The other would be value semantics, i.e. copying the pointer makes a new copy of the pointed to object. This signature wouldn't work with a pointer like auto_ptr/unique_ptr.
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
I'm in the process of writing a smart pointer countedptr and I've hit a speed bump. The basic function of countedptr is to work like any other smart pointer and also have a count of how many pointers are pointing to a single object. So far, the code is:
[SOLVED]
#include "std_lib_facilities.h"
template <class T>
class counted_ptr{
private:
T* pointer;
int* count;
public:
counted_ptr(T* p = 0, int* c = new int(1)) : pointer(p), count(c) {} // default constructor
explicit counted_ptr(const counted_ptr& p) : pointer(p.pointer), count(p.count) { ++*count; } // copy constructor
~counted_ptr() { --*count; delete pointer; }
counted_ptr& operator=(const counted_ptr& p)
{
pointer = p.pointer;
count = p.count;
++*count;
return *this;
}
T* operator->() const{ return pointer; }
T& operator*() const { return *pointer; }
int Get_count() const { return *count; }
};
int main()
{
counted_ptr<double> one;
counted_ptr<double>two(one);
int a = one.Get_count();
cout << a << endl;
}
When I try to do something like
one->pointer = new double(5);
then I get a compiler error saying "request for member 'pointer' in '*(&one)->counted_ptr::operator->with T = double' which is of non-class type double".
I considered making a function to do this, and while I could make a function to allocate an array of T's, I can't think of a way of making one for allocating actual objects. Any help is appreciated, thanks.
Old Solution
What about another assignment operator?
counted_ptr& counted_ptr::operator=(T* p)
{
if (! --*count) { delete count; }
pointer = p;
count = new int(1);
return *this;
}
...
one = new double(5);
Also, your destructor always deletes a shared pointer, which is probably what caused *one to be a random nomber. Perhaps you want something like:
counted_ptr::~counted_ptr() { if (! --*count) { delete pointer; delete count; } }
New Solution
As you want repointing a counted_ptr (eg one = new double(5)) to update all related counted_ptrs, place both the pointer and the count in a helper class, and have your pointer class hold a pointer to the helper class (you might already be headed down this path). You could go two ways in filling out this design:
Make the helper class a simple struct (and a private inner class) and place all the logic in the outer class methods
Make counted_ptr the helper class. counted_ptr maintains a reference count but doesn't automatically update the count; it's not a smart pointer, it only responds to release and retain messages. If you're at all familiar with Objective-C, this is basically its traditional memory management (autoreleasing aside). counted_ptr may or may not delete itself when the reference count reaches 0 (another potential difference from Obj-C). counted_ptrs shouldn't be copyable. The intent is that for any plain pointer, there should be at most one counted_ptr.
Create a smart_ptr class that has a pointer to a counted_ptr, which is shared among smart_ptr instances that are supposed to hold the same plain pointer. smart_ptr is responsible for automatically updating the count by sending its counted_ptr release and retain methods.
counted_ptr may or may not be a private inner class of shared_ptr.
Here's an interface for option two. Since you're doing this as an exercise, I'll let you fill out the method definitions. Potential implementations would be similar to what's already been posted except that you don't need a copy constructor and copy assignment operator for counted_ptr, counted_ptr::~counted_ptr doesn't call counted_ptr::release (that's smart_ptr::~smart_ptr's job) and counted_ptr::release might not free counted_ptr::_pointer (you might leave that up to the destructor).
// counted_ptr owns its pointer an will free it when appropriate.
template <typename T>
class counted_ptr {
private:
T *_pointer;
size_t _count;
// Make copying illegal
explicit counted_ptr(const counted_ptr&);
counted_ptr& operator=(const counted_ptr<T>& p);
public:
counted_ptr(T* p=0, size_t c=1);
~counted_ptr();
void retain(); // increase reference count.
bool release(); // decrease reference count. Return true iff count is 0
void reassign(T *p); // point to something else.
size_t count() const;
counted_ptr& operator=(T* p);
T& operator*() const;
T* operator->() const;
};
template <typename T>
class smart_ptr {
private:
counted_ptr<T> *_shared;
void release(); // release the shared pointer
void retain(); // retain the shared pointer
public:
smart_ptr(T* p=0, int c=1); // make a smart_ptr that points to p
explicit smart_ptr(counted_ptr<T>& p); // make a smart_ptr that shares p
explicit smart_ptr(smart_ptr& p); // copy constructor
~smart_ptr();
// note: a smart_ptr's brethren are the smart_ptrs that share a counted_ptr.
smart_ptr& operator=(smart_ptr& p); /* Join p's brethren. Doesn't alter pre-call
* brethren. p is non-const because this->_shared can't be const. */
smart_ptr& operator=(counted_ptr<T>& p); /* Share p. Doesn't alter brethren.
* p is non-const because *this isn't const. */
smart_ptr& operator=(T* p); // repoint this pointer. Alters brethren
size_t count() const; // reference count
T& operator*() const; // delegate these to _shared
T* operator->() const;
};
Hopefully, the only ambiguous points above are the intentional ones.
(Sorry, newbie here, and can't leave comments). What Adatapost added, "one=new double(5);" should work. One other change needed, though: the reference counting needs a little help.
...
~counted_ptr() {
--*count;
// deallocate objects whose last reference is gone.
if (!*count)
{
delete pointer;
delete count;
}
}
counted_ptr& operator=(const counted_ptr& p)
{
// be careful to accommodate self assignment
++*p.count;
// may lose a reference here
--*count;
if (!*count)
{
delete pointer;
delete count;
}
count=p.count;
pointer=p.pointer;
return *this;
}
Of course, there's some code repetition here. It might make sense to refactor that code into its own function, e.g.
private:
/** remove our reference */
void release()
{
--*count;
if (!*count)
{
delete pointer;
delete count;
}
}
Did you, perhaps, mean "one.pointer=new double(5);"? Writing "one->pointer=new double(5);" invokes counted_ptr<double>::operator->. That is, it is approximately equivalent to:
double *tmp = one.operator->(); // returns one.pointer
tmp->pointer = new double(5);
But a double pointer isn't a structure, and so it doesn't have a pointer member.
Since the immediate problem has already been solved, I want to offer something more long term:
As you continue to develop this code, you'll definitely want to offer it up for full review by experienced programmers, whether here or elsewhere. There were a few obvious problems with your code as you posted it, though outis has helped correct them. But even once your code all compiles and seems to work in your own tests, there may be tests and situations which you haven't yet learned to think about. Smart pointers can easily have subtle problems that don't show up until very specific situations. So you'll want others to look over your code to find anything which you may have missed.
Please don't take this as any kind of insult towards your current code. I'm just offering this as friendly advice to ensure you learn the most you can out of this project.
Unless you are not doing this for academic reasons, you might want to use consider using the use_count() member of boost::shared_ptr. It's not entirely efficient, but it does work and you're better off using something well tested, mature, and thread safe. If you are doing this for learning purposes, be sure to check out the treatment of Reference Counting and Smart Pointers in More Effective C++.
You need to decrement the count and possibly delete the pointer to the old value in operator = before you overwrite it. You also need 'delete count' everywhere you have 'delete pointer' to avoid leaking memory