Binary Tree: Copy Constructor - c++

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.

Related

Using an unique_ptr as a member of a struct

So I have a struct like this:
struct node {
node_type type;
StaticTokensContainer token_sc;
std::unique_ptr<node> left; // here
std::unique_ptr<node> right; // and here
};
When I tried to write a recursive function to convert a node to string for printing like this:
std::string node_to_string(node n) {
return "<other members converted to strings>" + node_to_string(*(n.left));
}
It gives me: function "node::node(const node &)" (declared implicitly) cannot be referenced -- it is a deleted function
I don't understand why this error is showing up, I had no problem when I did (*n) when I tried passing n as std::unique_ptr<node>, so I don't see why doing *(n.left) is not allowed.
you pass node by value, and you cannot set one std::unique_ptr equal to another:
int main() {
std::unique_ptr<node> a = std::make_unique<node>();
std::unique_ptr<node> b = a; //Error C2280
}
because who is now the unique owner of the data?
To fix your error, simple take a const reference in your function. This way, you pass the reference and that won't create copies:
std::string node_to_string(const node& n) {
return "<other members converted to strings>" + node_to_string(*(n.left));
}
(you should also think about exiting this function, otherwise you will get either get a stack overflow or a dereferenced nullptr, whichever comes first. For example, add if (!n.left) return "";)
The compiler error basically tells you that the copy-constructor of struct node is declared as implicitly deleted. To understand clearly what this means let's consider the following example.
struct node {
node_type type;
StaticTokensContainer token_sc;
node* left; // here
node* right; // and here
};
If you try the same thing with this struct declaration, you wil see that you get no error. So the problem must lie within the use of std::unique_ptr.
Here we can see that there is written.
The implicitly-declared or defaulted copy constructor for class T is defined as deleted if any of the following conditions are true:
T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors);
...
The std::unique_ptr cannot be copied, as it is stated here
The class satisfies the requirements of MoveConstructible and MoveAssignable, but of neither CopyConstructible nor CopyAssignable.

Address of pointer return value

I have the following .h file
class Node
{
private :
int m_data;
Node* m_left;
Node* m_right;
public:
Node(int data) : m_data(data), m_left(nullptr), m_right(nullptr) {}
Node* getLeft() { return m_left; }
Node* getRight() { return m_right; }
int getData() { return m_data; }
};
class Tree
{
private :
Node * m_root;
unsigned int m_size;
void freeNode(Node*);
bool insert(Node**, int );
public:
Tree() : m_size(0) {}
~Tree();
int findMaximumVericalSum();
bool insert(int);
};
The implementation, I get an error - what is wrong and how should I fix it
'&' requires l-value
The address-of operator ( & ) must have an l-value as operand.
bool Tree::insert(Node** root, int data)
{
Node* newNode = new Node(data);
if (*root == nullptr) {
*root = new Node(data);
}
else if (data < (*root)->getLeft()->getData())
{
// error '&' requires l-value
insert(&((*root)->getLeft()), data);
}
else if (data > (*root)->getLeft()->getData())
{
}
}
To my surprise, I wasn't able to find a good duplicate for the question as asked (other issues notwithstanding).
The error you see comes from the following piece of code:
&((*root)->getLeft())
And can be vastly simplified into following very little piece:
int bar();
void foo(int*);
void baz() {
foo(&bar());
}
(also can be seen on https://gcc.godbolt.org/z/PuwtuT)
This produces exactly the same compiler error. And the reason for this is that C++ prohibits taking an address of prvalues, and while whole rvalue/lvalue discussion is super complicated, it is easy to remember that when function returns a value, it is always an prvalue. (If function returns a reference, this is a different story). You can, however, pass an rvalue by either const or rvalue reference.
I can not immediately offer a solution to your problem, because I am not super clear what is your end goal. But I do hope that this answer could at least steer your thoughts and allow you to come up with another question.
Node* getLeft()
This function returns a prvalue.
return m_left;
The prvalue is initialised from this expression. It is not possible to get address of m_left from the prvalue. More generally, it is not possible to get the address of any prvalue. The operand of the addressof operator must be an lvalue.
&((*root)->getLeft()
what is wrong
Here, the operand of the addressof operator is a prvalue. The program is ill-formed.
how should I fix it
Don't try to apply the addressof operator to a prvalue expression.
If your intention is to pass a pointer to (*root)->m_left into the function, then you need some way to access that member.
A solution is to give access to the member by it public, or through friendship.
Another is to provide a public getter that returns a reference, rather than a copy of the member.
A third solution is to add a setter function. In that case, you can use a local variable as argument to the recursion, and then call the setter with the variable after it was modified by the function.
Furthermore, Tree::insert is declared to return bool, but lacks any return statements. Behaviour of the program is undefined (or would be if it was well-formed in the first place).
P.S. Your function doesn't handle the case of data == (*root)->getLeft()->getData().

How do I properly dereference an object to set it's member variable in the following case?

I'm implementing a tree by writing a class called Node, and I'm having issues concerning the following method and member variables:
class Node {
public:
// ...
void addChild(const std::shared_ptr<Node> node);
private:
// ...
std::vector< std::shared_ptr<Node> > _children;
std::shared_ptr<Node> _parent;
};
When addChild is called by a Node object, it's simply supposed to set the argument's _parent member to point to the caller, then add the argument to the caller's vector of children. My implementation currently looks like this:
void Node::addChild(const std::shared_ptr<Node> node) {
if (!node) {
std::invalid_argument("Argument node must be nonnull.");
}
*node._parent = this;
_children.push_back(node);
}
The troublesome line is *node._parent = this;, which causes this error:
src/Node.cpp:89:10: error: no member named '_parent' in
'std::__1::shared_ptr<Node>'; did you mean to use '->' instead of '.'?
My train of thinking is telling me that I'm dereferencing node to gain access to the actual Node object, then using ._parent to gain access to the _parent member. Since _parent is a std::shared_ptr<Node>, it would then make sense to set it to equal this, which is a pointer to the calling object. But why is my compiler telling me that _parent doesn't exist when it definitely does exist?
Edit: I've also already attempted (*node)._parent, node->_parent, (*node)->_parent, and *(*node)._parent, all to no avail. They all result in error: no viable overloaded '='.
Your line of code: *node._parent = this; has two issues:
operator precedence
assignment to shared_ptr
First operator precedence. Operator . is evaluated before operator *. So currently your code is similar to doing this:
*(node._parent)
node doesn't have a _parent member, but *node does. So, fix it by either:
(*node)._parent
Or even better:
node->_parent
Then, you have another issue that you can't just assign to shared_ptr with operator =() like this. You need to change
= this;
To:
.reset(this);
So in total,
node->_parent.reset(this);

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).

Const-correctness with a linked list

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