Const-correctness with a linked list - c++

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; }

Related

I want to bubble sort a singly linked list in c++, but i keep facing a lvalue error and i am unable to resolve it

Here is the code that might help you understand my problem, i am using a node class and a list class. Here is the error:[Error] lvalue required as left operand of assignment. any help would be appreciated..!!
class Node{
private:
int data;
Node* next;
public:
void SetNext(Node* next){
this->next=next;
}
void SetData(int data){
this->data=data;
}
Node* GetNext(){
return next;
}
int GetData(){
return data;
}
};
class List{
private:
Node *CurrentLocation;
public:
List(){
CurrentLocation=NULL;
}
void SortList(){
Node *t1,*t2;
int temp;
for(t1=CurrentLocation;t1!=NULL;t1=t1->GetNext()){
for(t2=CurrentLocation;t2!=NULL;t2=t2->GetNext()){
if(t2->GetData()>t1->GetNext()->GetData()){
temp=t2->GetData();
t2->GetData()=t2->GetNext()->GetData();
t2->GetNext()->GetData()=temp;
}
}
}
}
};
You are trying to assign to an rvalue.
This rvalue is a temporary copy of your data. not the original one. It will be destroyed as soon as the expression executes, hence u can't assign to it.
To assign to your data, u must return a reference to it, as follows
int& GetData(){
return data;
}
As user4581301 has mentioned, I advise you to see this What are rvalues, lvalues, xvalues, glvalues, and prvalues?
The previous description was upon the problem itself but It would be better to write a setter for your data like your getter, as follows
void setData(int newData){
data = newData;
}
Here's the problem line:
t2->GetData()=t2->GetNext()->GetData();
The issue is that the GetData() function returns a copy of the member data which has no local storage (you haven't assigned it to a named variable, hence it's an rvalue). The reason the compiler complains is because really this does nothing. You're assigning a new value to a temporary value that's going away immediately after the line executes.
Converting the member function to
int& GetData(){
return data;
}
means when you call t2->GetData() it actually refers to the data member of t2 and it isn't just a temporary that goes away.
Really though it's probably just better to make data a public member variable so you can just set it/read from it directly
This line is problematic:
t2->GetData()=t2->GetNext()->GetData();
You're trying to assign a value into the data int. However, the GetData member function don't return data itself: is instead returns a temporary copy. You cannot assign into temporaries. To grossly simplify, the compiler call temporaries as rvalue, and non temporary as lvalue. Assignments need lvalues, as it would make little sense to assign into temporaries.
The best would be to use the setter function to set the value:
t2->SetData(t2->GetNext()->GetData());
Here the setter will assign into the data data member.

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_} {}

Binary Tree: Copy Constructor

I have to create a copy constructor for a binary tree with the signature bstt(const bstt& other). I attempted to do so, but I get the following error (in the last code block).
I thought I needed to change my helper function signature to include a const, but I tried a few combinations and none of them worked. How can I fix this?
Helper function:
void _postordercopy(NODE*& thisT, const NODE*& otherT)
{
if(otherT==nullptr){
thisT=nullptr;
}
else
{
NODE* tmp=new NODE;
tmp->Key=otherT->Key;
tmp->Value=otherT->Value;
tmp->Left=otherT->Left;
tmp->Right=otherT->Right;
tmp->isThreaded=otherT->isThreaded;
_postordercopy(thisT->Left,otherT->Left);
_postordercopy(thisT->Right,otherT->Right);
}
}
Copy Constructor:
bstt(const bstt& other)
{
Size=other.Size;
_postordercopy(Root,other.Root);
}
Error Message:
bstt.h:110:35: error: no matching function for call to ‘bstt<int, int>::_postordercopy(bstt<int, int>::NODE*&, bstt<int, int>::NODE* const&)’
You are creating a tmp node in your _postordercopy function, assigning to it, but aren't doing anything with it. Further, you are copying over the exact pointers for Left and Right. That doesn't look right either.
I think this is what you really want for a recursive "copy the tree" function:
NODE* copyTree(const NODE* other)
{
NODE* newTree = nullptr;
if (other!=nullptr)
{
newTree = new NODE;
newTree->Key = other->Key;
newTree->Value = other->Value;
newTree->isThreaded = other->isThreaded;
newTree->Left = copyTree(other->Left);
newTree->Right = copyTree(other->Right);
}
return newTree;
}
And then in your copy constructor, just invoke it as follows:
bstt(const bstt& other)
{
this->Size = other.Size;
this->Root = copyTree(other.Root);
}
I will assume that bstt::Root is declared as NODE*, as this seems most likely.
The second parameter to _postordercopy is of type const NODE*&, a reference to a pointer to a constant NODE. What is the type of the argument you try to pass in? Since other is declared const, each of its members is const. So other.Root is a constant pointer to NODE, also known as NODE* const – the const is tacked on the far right of the declared type. This is incompatible with the parameter type. (See What is the difference between const int*, const int * const, and int const*? for a more thorough discussion of this distinction.)
The problem is that a non-constant reference cannot be initialized from a constant. Since other has to be const, you'd need to change the type of the function parameter to match what you give it. One solution is to move const so that it qualifies the pointer rather than the pointer-to object: NODE* const &. Another solution is to remove the ampersand, as that is a bit wasteful in this case: const NODE*. (The reference adds an extra layer of indirection that provides no benefit.) Think about what your function is supposed to do, and const-qualify whatever is supposed to not change. In this case, the node pointed to by the second parameter should not change.
While that resolves your immediate compiler error, there are other errors to address in your code. In addition, I would consider adding two accessor functions to get the root node. These functions could enforce const in a way that you do not get when accessing Root directly.
NODE *& get_root() { return Root; }
const NODE * const & get_root() const { return Root; }
The main difference is that the type of other.Root is NODE * const, which drops the const-qualifier for the node, whereas other.get_root() would yield a const NODE * const, which propagates the qualifier. At the same time, this->get_root() would yield a simple NODE* since this is not const-qualified. You get to use the same syntax, and the const-ness is propagated appropriately.

Initialise non-const pointer with this in const member function

If have a class Link for a linked list implementation:
class Link {
public:
string value;
// constructor etc.
Link* insert(Link* n);
// other member functions
Link* advance(int n) const;
private:
// ...
}
In the const member function Link* advance(int n) const, I want to advance by n elements in the list and get a pointer to that element. To that end, I want to initialise a pointer with the this pointer:
Link* Link::advance(int n) const {
if (this==0) return 0;
Link* p = this;
// do stuff with p
return p;
}
This doesn't work, because Link* p = this; is trying to initialise a Link* with a const Link*. If I change it to const Link* p = this; however, my return p; returns the wrong type.
I think the function should be const as it doesn't change anything, but I also want it to return a non-const pointer, because I want to use it to modify elements in my list. Is there a better way than something ugly such as
Link* Link::advance(int n) const {
if (this==0) return 0;
const Link* p = this;
// do stuff with p
return const_cast<Link*>(p);
}
to achieve this?
If you want to MODIFY elements in your linked list, the function should NOT be const in the first place, since a const function is based on the fact that you are not modifying the data in the object you pass in (and if that's a linked list, it also applies to any subsequent members of the list). You should also not leak objects that allow your class to be modified, such as returning a pointer or reference to elements within the object.
If you actually want to modify the contents of the list in this function, then make the function non-const. If you want to have a function that is const for practical reasons (passing a const value in), then have a second variant that is const and doesn't modify the content (and returns a const Link*).

Const function eventually modifies itself through friend class [duplicate]

This question already has answers here:
c++ passing by const reference
(4 answers)
Closed 8 years ago.
In my implementation I have a main class Tree which contains many Nodes. Each Node contains a pointer to the next Node, which can be NULL if not implemented. Tree keeps track of the total amount of nodes.
Through the Tree a search function is implemented which returns the Node pointer. However if the Node does not exist yet, a new node is created. This increments the Tree::m_totalnodes variable. However, Tree::GetNode is const, yet this modification does not provide any warnings, errors or runtime failures. What is the reason for this (maybe related that tbNode::m_tree is non-const)?
class Node; //forward declare
class Tree {
public:
Tree() : m_totalnodes(0) {
m_firstnode = new Node(this,0);
}
Node* GetNode(int nodenumber) const { //note const! Should not modify Tree
return m_firstnode->FindNode(nodenumber); //this function eventually modifies it (and no warning)
}
private:
friend class Node;
unsigned int m_totalnodes;
Node* m_firstnode;
};
class Node {
public:
Node(Tree *thetree, int nodenumber) : m_tree(thetree), m_nextnode(NULL), m_nodenumber(nodenumber) {
m_tree->m_totalnodes++;
}
Node* FindNode(int nodenumber) {
if (!m_nextnode)
m_nextnode = new Node(m_tree, nodenumber);
return m_nextnode;
}
private:
Tree* m_tree;
Node* m_nextnode;
unsigned int m_nodenumber;
};
Node* m_firstnode;
When you call a const method like GetNode, this becomes const-qualified to type Tree const *. When you access members like m_totalnodes, they appear to be const-qualified like unsigned int const. You cannot modify the object. m_firstnode becomes Node * const: you cannot modify the pointer, but you can still modify the Node it points to.
If you want to comprehensively avoid accidental modification, access m_firstnode through an overloaded getter function:
Node * & firstnode() { return m_firstnode; }
Node const * firstnode() const { return m_firstnode; }
(Other accessor styles are possible, this is just one example. But note, I don't think it would be safe to return Node const * const & from the second accessor; this may compile but it would return a reference to a temporary.)
Non-const members will call the first method, and will see a modifiable pointer pointing to a modifiable Node. Const-qualified members will call the second, and see neither as modifiable.
That const only promises that the method itself is not going to change the data members. It does not say anything about a Node object not changing data members (which the Node can, since it's a friend).