How to have this const-corrected? - c++

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.

Related

Using of const for std::pair in c++

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.

Return value type does not match the function type CONST

Not sure why error is happening on this function when I made the variable length const on a different function
int list::length() const {
return length;
}
CPP File Above
Header file BELOW
class list {
private:
struct Node
{
int info;
Node *next;
};
int length;
Node *head;
public:
list();
list(const list& otherlist);
~list();
int list::length() const;
};
The first issue is that you are qualifying length() inside your class. Remove list::. The second issue is that your member variable length, and your member function length are conflicting, because they have the same name.
I'd rename your members so that they don't clash with the functions. Consider length_ and head_.
Edit: please also post compiler errors in the future, as it makes other readers' jobs a little easier :)

Class forward declaration leads to incomplete type

I have the following code:
class Node;
class Edge {
public:
//Edge(Node originnode,Node targetnode,int weight):OriginNode(originnode),TargetNode(targetnode),Weight(weight){};
//~Edge(){};
//Node getOriginNode() const { return OriginNode;};
//Node getTargetNode() const { return TargetNode;};
int getWeight() const { return Weight;};
Node OriginNode;
Node TargetNode;
int Weight;
};
class Node{
public:
std::string getName();
std::vector<Edge> getEdges();
};
when I am trying to compile the compiler claims that OriginNode and TargetNode have incomplete type. I have already forward declare the Node. How can I fix that?
You can't without providing a full definition of Node. You can alternatively declare pointers as members: Node* OriginNode.
I'd suggest you re-think your design though - does an Edge really have a Node. As in - does it have ownership over it? Can't a Node be shared between multiple edges? Also, Does a Node really contain multiple edges?
With boost::container you can change the order of the declarations because it support containers of incomplete types :
class Edge;
class Node{
public:
std::string getName();
boost::container::vector<Edge> getEdges();
};
class Edge {
public:
//Edge(Node originnode,Node targetnode,int weight):OriginNode(originnode),TargetNode(targetnode),Weight(weight){};
//~Edge(){};
//Node getOriginNode() const { return OriginNode;};
//Node getTargetNode() const { return TargetNode;};
int getWeight() const { return Weight;};
Node OriginNode;
Node TargetNode;
int Weight;
};
By the way, why do you want to return a value of the Edge vector when you get them from the Node and why do you store the value of the OriginNode and TargetNode inside the edge, you can use references or pointers instead (maybe I'm wrong, I don't know all your requirements).
You should reverse the order of declarations
class Edge;
class Node{
public:
std::string getName();
std::vector<Edge> getEdges();
};
class Edge {
public:
//Edge(Node originnode,Node targetnode,int weight):OriginNode(originnode),TargetNode(targetnode),Weight(weight){};
//~Edge(){};
//Node getOriginNode() const { return OriginNode;};
//Node getTargetNode() const { return TargetNode;};
int getWeight() const { return Weight;};
Node OriginNode;
Node TargetNode;
int Weight;
};
Nevertheless I consider that design of classes as very bad. Node is a primitive entity that should not contain a vector of Edge. Why should Node control the container?! The container is a more high -level generalization than Node. So it should be placed in a more high level compared with Node.

Sorting in C++ using defined class

I'm kind of new at C++, so bear with me if you can. I'm trying to sort a vector full of nodes. In my .h file I have the following definition for a node:
class Node{
public:
Node(int data);
bool sortMe(const Node & n1, const Node & n2);
int getData();
private:
int nData;
};
In my .cpp file, I define the functions such as:
Node::Node(int data){
this->nData = data;
}
bool Node::sortMe(const Node & n1, const Node & n2){
return n1.nData < n2.nData;
}
and in main attempt to sort a vector with:
Node aNode(7);
Node bNode(90);
Node cNode(84);
std::vector<Node> arrayName;
arrayName.push_back(aNode);
arrayName.push_back(bNode);
arrayName.insert(arrayName.begin(), cNode);
std::sort(arrayName.begin(), arrayName.end(), &Node::sortMe);
I include algorithm and everything, I just can't figure out why it doesn't want to use that function to sort the data...
Thanks in advance!
sortMe() is currently declared as a member function. That means it needs to be called on a specific instance of the Node class, rather than being used as a standalone function.
To fix it, simply prefix the function declaration with static (only in the class header; not in the implementation). That means the function belongs to the class, not a specific instance.
You can use boost::bind:
std::sort( arrayName.begin(), arrayName.end(),
boost::bind(&Node::sortMe, this,_1,_2));
or make sortMe function static.
Even better is to use a functor instead of a function (this will be faster):
class Node{
public:
Node(int data);
bool sortMe(const Node & n1, const Node & n2);
int getData();
struct doCompare
{
bool operator()( const Node& n1, const Node& n2 )
{
// comparison code
}
};
private:
int nData;
};
std::sort( arrayName.begin(), arrayName.end(), Node::doCompare() );
The std::sort() need a reference of comparison function.
There will be two ways to implement the comparison function:
static member function
A normal member function shall not be used since a member function must need a specific instance of its class.
class Node{
public:
static bool sortMe(const Node & n1, const Node & n2);
};
normal function().
Remove the 'bool sortMe(const Node & n1, const Node & n2);' out from the Node class to the main.cpp, the problem will be solved.
bool Node::sortMe(const Node & n1, const Node & n2){
return n1.nData < n2.nData;
}
If you use C++11, you can use lambda.
std::sort(arrayName.begin(), arrayName.end(), [](Node& l, Node& r){ return l.getData() < r.getData(); });

I keep getting confused about proper encapsulation with regard to pointers, could someone take a look?

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