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.
Related
I am trying to use polymorphism to represent a tree of sorts (terminal and non-terminal nodes for a parser). One of the classes Node holds a vector of smart pointers to subclasses of Base. I'd like to add more nodes to this vector with a method that accepts just a Node reference without having to create the smart pointer outside of the class (or is this terrible practice?).
The void addNode(const std::unique_ptr<Node> &&node) overload works, but I'd like to use the
void addNode(const Node &&node) overload.
No matter what I do, I can't seem to create and move this smart pointer into the vector without breaking the copy elision rule or calling the copy constructor of Node and I'm not exactly sure why. It looks like make_unique is always invoking the copy-constructor of Node. I've tried using these statements:
nodes.push_back(std::make_unique<Node>(node));
Gives a compiler error call to implicitly-deleted copy constructor. This makes sense, thought I just had to std::move it.
nodes.push_back(std::move(std::make_unique<Node>(node)));
Same error as above, also: moving a temporary object prevents copy elision.
nodes.push_back(std::make_unique<Node>(std::move(node)));
Just gives the call to implicitly-deleted copy constructor.
nodes.push_back(std::move(std::make_unique<Node>(std::move(node))));
Even tried this, which gives both errors as well.
Here is my repro:
#include <iostream>
#include <string>
#include <vector>
class Base {};
class Node;
typedef std::vector<std::unique_ptr<Base> > Nodes;
class Node : public Base {
Nodes nodes;
public:
Node() {};
void addNode(const Node &&node) {
nodes.push_back(std::make_unique<Node>(node));
//nodes.push_back(std::move(std::make_unique<Node>(node)));
//nodes.push_back(std::make_unique<Node>(std::move(node)));
//nodes.push_back(std::move(std::make_unique<Node>(std::move(node))));
}
void addNode(std::unique_ptr<Node> &&node) {
nodes.push_back(std::move(node));
}
};
int main()
{
Node rootNode;
rootNode.addNode(Node());
rootNode.addNode(std::make_unique<Node>());
return 0;
}
I am compiling on MacOS with Clang++ and C++20.
How do you want to move const object?
It should be:
void addNode(Node &&node) {
nodes.push_back(std::make_unique<Node>(std::move(node)));
}
by move, you modify moved instance, so it should be modifable.
Demo
The error is due to void addNode(const Node &&node) taking a const rvalue reference, which are a big oddity of extremely dubious utility (there are a few corner cases they might make sense, see this answer for more).
Given that you can't move from a const Node&& (it is constant, after all), the implicitly generated move constructor (Node(Node&&)) fails to match.
Due to an old quirk inherited from C++03 const T& can bind to rvalues, so a const T&& can decay to const T&.This causes the compiler to attempt to invoke Node(const Node&), aka the copy constructor, which is deleted due to the fact that std::vector<std::unique_ptr<Base>> is not moveable, which is also due to std::unique_ptr<T> not being moveable.
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.
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.
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);
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).