0

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.

Jordan
  • 902
  • 2
  • 10
  • 37

2 Answers2

4

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

rafix07
  • 20,001
  • 3
  • 20
  • 33
1

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.

mcilloni
  • 693
  • 5
  • 9