For implementing binary search tree I am wandering why we put Const before K in std::pair ?
template<class K,class V>
class BST {
public:
class Node {
public:
pair<const K, V> data; // pair < data_type1, data_type2 > Pair_name;
unique_ptr<Node> left; // Create a unique_ptr object through raw pointer
unique_ptr<Node> right;
unique_ptr<Node> parent;
Node(const K& k, const V& v): //constructor
data{std::move(k),std::move(v)}, right(nullptr),left(nullptr),parent(nullptr)
{
}
~Node() noexcept = default;
Because, logically, the node keys in your data structure must be immutable: immune from change. That's to maintain the proper structural layout, which depends on the relative ordering (by key!) of the nodes.
If a node needs to be renamed, higher-level algorithms need to be deployed in order to do that.
Sure, there's no code in your snippet that would even try to modify a key — and why would there be? It wouldn't compile! — but putting the const there enforces that at compile-time if anybody forgets and tries to do it. So, it's for the same reason as any other usage of const: enforcing immutability.
std::map containers also store std::pair<const K, V>, for just the same reason.
Related
I've created a Node struct to be used in an implementation of binary search tree. It uses shared pointers to keep track of its children:
template <class T> struct Node;
template <class T>
using Node_ptr = std::shared_ptr<Node<T>>;
template <class T>
struct Node {
Node_ptr<T> left;
Node_ptr<T> right;
const T label;
...
};
Now, I'd like to have a function which given some subtree and a value will return either the node of that specific value or the place where such node should be located in future - find_node.
This is how it looks at the moment:
template <class T>
auto* find_node(Node_ptr<T>* node, const T& value) {
for (; *node && (*node)->label != value
; node = value < (*node)->label ? &(*node)->left : &(*node)->right);
return node;
}
Pretty bad. But it works:
template <class T>
class Binary_search_tree {
public:
// correctly inserts consecutive values
void insert(const T& value) {
if (auto* node = find_node(&root, value); !*node)
*node = std::make_shared<Node<T>>(value);
}
...
private:
Node_ptr<T> root;
...
};
I could rewrite find_node to use std::shared_ptr<Node_ptr<T>> instead of Node_ptr<T>* but it would look even worse. Or would it?
How should I handle such situations?
edit: As it's been pointed out, the function can be simplified a bit by taking a reference to starting node, and returning a reference to a node:
template <class T>
Node_ptr<T>& find_node(Node_ptr<T>& node_ref, const T& value) {
auto* node = &node_ref;
...
return *node;
}
Use of a raw pointer is suggested when you have to allow for passing a null pointer - not true in your example; or when you have to pass a non-integral value (true in your case). In the latter case one should still consider passing a reference rather than a raw pointer. This is a generic suggestion - so exceptions may exist.
Having noted that, you could still use a raw pointer in your function here rather safely by making find_node(...) a private function while keeping the insert(...) public. That is safe, since there is no chance of leaving the pointer dangling from inside insert(...).
Essentially we need to guard against two possibilities with raw pointers: #1. prematurely deleting the memory the pointer points to, #2. never deleting the memory that the pointer points to. Neither of this is possible inside your insert(...) function. So you're safe.
On a related note, you might consider having unique_pointer for your nodes when they are created and then converting them into shared pointers if they are to be shared by more child than one: std::move(...).
Working on a tree class with an STL-like interface, I encountered a problem using non default-constructible elements:
As I'm implementing iterators, I need a past-the-last element at any point in time. My approach is to create one in the constructor, which asserts the value type to be default constructible.
Is there an approach to get rid of this constraint?
Pointing past the end can't possibly be nullptr if your iterator is bidirectional, end()-- needs to be legal.
It can be instead implemented by a sentinel, and in this case, the sentinel shouldn't even contain a default constructed element.
This can be done as such
struct link
{
link *parent, *left, *right;
};
template<typename T>
struct node : link
{
T data;
};
template<typename T>
struct tree : link
{
// tree itself serves as the sentinel
// At initialization parent and childs should all point to the sentinel
tree() : parent(this), left(this), right(this) {}
// ...
};
And iterators need no special handling for one past the end case.
// nested within tree
struct iterator
{
explicit iterator(link* l) : n(l) {}
iterator& operator--() { n = n->parent; return *this; } // or something else
auto& operator*() { return reinterpret_cast<node<T>*>(n)->data; }
// ...
link* n;
};
iterator begin() { return {left}; } // or something else
iterator end() { return {this}; }
I solved the problem by storing another pointer to the root node, from which I can recreate the last element in constant time.
I have a const-correctness problem which I don't seem to be able to resolve. Here is the structure of my program:
class Node
{
private:
int id;
std::set<Node*> neighbours;
public:
Node();
Node(int id_p);
void set_id(const int& id_p);
int get_id() const;
void add_neighbour(Node* neighbour);
bool is_neighbour(Node* neighbour) const;
friend bool operator <(const Node& lhs, const Node& rhs);
};
class Graph
{
private:
std::set<Node> node_list;
public:
Graph();
void add_node(int id);
const Node* get_node_by_id(int id) const;
bool has_node(int id) const;
void check_add_node(int id);
void add_edge(int id_1, int id_2);
bool has_edge(int id_1, int id_2) const;
void check_add_edge(int id_1, int id_2);
(...)
};
Now the thing is, if I call the function Graph::get_node_by_id(), I want to return a pointer to a given node (type Node). But it seems impossible to do so, because the std::set implicitly converts my Node type objects to const Node objects, and I am unable fetch a non-const pointer from a const object.
However, I cannot have everything else set to const Node (which would resolve the problem), because I want to call Node::add_neighbour() from Graph::add_edge(), but whenever I do so, my compiler says that I might be violating the constness (required to have a sorted set) of the elements in the node_list set, even though I defined the less operator< to only care about the id.
Is there anything I can do to resolve this dilemma (without giving up on having a sorted set)? Thank you for your responses!
More info on the error:
If I use non-constant fields, error in Graph::get_node_by_id():
for(Node& element : this->node_list) // Error: element should be const Node&
{
if(element->get_id() == id)
{
return element;
}
}
return nullptr;
If I use constant fields, error in Graph::add_edge():
(...)
const Node* node_1 = this->get_node_by_id(id_1);
const Node* node_2 = this->get_node_by_id(id_2);
node_1->add_neighbour(node_2); // Error for disregarding constness
node_2->add_neighbour(node_1);
Your issue appears to be that you have two different 'value semantics' to Node.
One is that exposed by operator< which is not affected by add_neighbour. This is the one set needs, to keep things ordered, and which it enforces by making Node const.
The other is that exposed by the class API, where both set_id and add_neighbour would change the value.
To keep your sorted set, you must not allow the id of a node to change once it's in the set. But you can allow the neighbours to change.
So I'd suggest you make the neighbours set mutable, make add_neighbour private and const, and make Graph a friend of Node.
This is what mutable gives you, data members that are not part of the 'value' of a type. Note that this means you are indicating that something holding a const Node* may expect the result of is_neighbour to change between calls.
So...
class Node
{
private:
// Trust Graph not to mess directly with these!
int id;
mutable std::set<Node*> neighbours;
friend class Graph;
// For Graph's exclusive use
void add_neighbour(Node* neighbour) const;
public:
Node();
Node(int id_p);
void set_id(const int& id_p); // Callable when not in Graph's set
int get_id() const;
void add_neighbour(Node* neighbour); // Callable when not in Graph's set
bool is_neighbour(Node* neighbour) const;
friend bool operator <(const Node& lhs, const Node& rhs);
};
class Graph
{
private:
std::set<Node> node_list;
public:
Graph();
void add_node(int id);
const Node* get_node_by_id(int id) const;
bool has_node(int id) const;
void check_add_node(int id);
void add_edge(int id_1, int id_2);
bool has_edge(int id_1, int id_2) const;
void check_add_edge(int id_1, int id_2);
(...)
};
Now what you have is public, non-const mutators for Node instances that aren't in Graph's set, and an extra mutator for Graph to use to change the neighbours of the Nodes in its set.
So only Graph can do
const Node b;
b.add_neighbour(nullptr);
If you really don't trust Graph, you can replace the private const add_neighbour with an inner class, with a static add_neighbour(Node* node, Node* neighbour method, since an inner class is implicitly able to access private data of the outer class.
class NeighbourHelper {
friend class Graph;
static void add(const Node* node, Node* neighbour) {
node->add_neighbour(neighbour);
}
Now only Graph can do
const Node b;
Node::NeighbourHelper::add(&b, nullptr);
In both cases, the following works for everyone:
Node a;
a.add_neighbour(nullptr);
At this point, you should be suffering a code-smell... The issue is the public get_node_by_id method in Graph. You actually probably want to expose an iterator of some kind instead, rather than than the raw Node*, and make Node a private inner class of Graph.
Or even just replace the whole Node concept with std::map<int,std::set<int>>...
But it depends on your actual use case.
Although TBBle's analysis is correct, there's a much simpler solution: replace Graph's std::set<Node> with std::map<int,Node>.
Your current Graph::get_node_by_id() is using a linear search because set doesn't really provide the lookup you want. Making the key external allows you to remove the operator< overload and still get a faster and more natural lookup: map.find(id).
The only ugly part is that now your Node has an internal id which must match an external key. If you never use the id except to look up a node in the map, you could just remove this entirely. If you need to follow the graph edges (neighbours) and then check the ID, you can replace your set of pointers with a set of map iterators, like:
typedef std::map<int, Node> NodeMap;
typedef std::set<NodeMap::iterator> NeighbourMap;
Then your traversal has the pair<const int,Node> available.
NB. on reflection, changing from set to map produces almost the same distinction as TBBle's answer: you split the Node into const and mutable parts. The lookup is cleaner with this solution (you can regain logarithmic-time lookup by constructing a fake Node as a key for set::find, but it's still a little inelegant), and the object identity is slightly cleaner with the other solution.
Let's suppose we have a RedBlack-Tree implementation which consists of 2 classes:
Tree - holds the pointer to the Node *root of the tree and defines all operations over the tree (Insert, Delete, etc)
Node - a data storage, which holds pointers to Node *parent, Node *left, Node *right nodes and std::string key.
The Tree::Insert() has the following implementation:
void Tree::Insert(const std::string &key)
{
Node *z = new Node(key);
// adding node logic
}
Now the task: every node has to store the time of its creation.
Limitations: the base tree implementation should be modified as less as possible and should contain details of specific extensions (so it should know nothing about the creation time property).
My thoughts: extending NodeWithTime : Node and adding unsigned int creation_time property.
Where I'm in stuck: how would we instantiate the node now?
Any proposals?
PS: it's neither a homework or a job task - I'm just learning c++ and data structures.
It's relatively simple. First, the Node struct:
template<typename T> struct Node {
Node(T t) : value(std::move(t)), time(RightNow()) {}
T value;
TimeType time;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
};
A quick helper make_unique:
template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args...)));
}
template<typename T> void Tree<T>::Insert(T key) {
auto z = make_unique<Node<T>>(std::move(key));
// insert
}
First, I fixed your crappy new and delete and replaced it with smart pointers. Then I also made your tree a template because who needs a tree that can only do one type? Then I swapped out your const T& with a T so that it might live with move-only types.
Then I just added a Time field and called RightNow() in the constructor. The exact TimeType and RightNow() you use depends on your needs and what exactly you mean by "time of it's creation". Are we talking about "6th July, 2013"? Or a very-high-resolution clock? In any case, these "creation time" details do not impact the tree.
Edit: Wait, you want to have one tree type where only some of the nodes know the creation time? Or just to alter the tree so that all the nodes know the creation time? I did #2, but for #1, you could indeed simply inherit from Node. To wit,
template<typename T> struct Node {
Node(T t) : value(std::move(t)) {}
T value;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
};
template<typename T> struct NodeWithTime : Node<T> {
TimeType time;
NodeWithTime(T t) : Node(std::move(t)), time(RightNow()) {}
};
template<typename T> void Tree<T>::insert(T t) {
std::unique_ptr<Node> nodeptr;
if (IWantToStoreCreationTime)
nodeptr = make_unique<NodeWithTime<T>>(std::move(t));
else
nodeptr = make_unique<Node>(std::move(t));
// insert
}
I keep finding myself wanting to do things the right way™. However, I am a bit confused about data encapsulation (not the principle of it, but how to do it correctly in C++)
Let's say I have the following class:
template <class T, class Alloc = std::allocator<T> >
class Tree
{
public:
class Node
{
public:
T data;
Node** get_children() const { return children; }
Node* get_parent() const { return parent; }
Node* get_right() const { return right; }
friend class Tree;
private:
Node** children;
Node* parent;
Node* right;
};
// typedefs for STL ...
class iterator // linear iterator
{
// ...
};
class const_iterator // linear iterator
{
// ...
};
// Tree operations ...
private:
Node root;
};
I want the tree to be able to modify the structure of the nodes freely, so I made it a friend class to the node.
I also want the user to be able to traverse the tree as a tree (rather than using the tree structure for storage behind the scenes, and just letting the user iterate over it linearly).
The data should be freely modifiable. If the owner of the tree object doesn't want another user to modify its contents, it can pass a const reference.
Now I'm left wondering about the return types of my getter methods. Whatever happens, I don't want the user ever to be able to change the structural information of the node directly. The tree should always be the one modifying the private members. Will this code guarantee that? What if instead of a Node**, we save a std::vector<Node*> and return a const reference to the vector?
These two are secure:
Node* get_parent() const { return parent; }
Node* get_right() const { return right; }
Since these functions return pointers by value, the user cannot modify the member variables.
This one is unsafe:
Node** get_children() const { return children; }
This too returns a pointer by value, so that the user cannot modify children, but the user can modify the elements in the array, which I presume are pointers to children. Here's how to make it safe:
Node * const * get_children() const { return children; }
(And when you're ready, you can advance to STL containers and stop using arrays.)