Why can't I insert a const pointer to a multiset? - c++

According to the documentation for multiset for instance see http://www.cplusplus.com/reference/set/multiset/insert/. It should be possible to insert a const value. In my example the multiset is a collection of pointers, but when I try to insert a const pointer I get an error.
template<typename Key>
struct node_key: public node_base {
node_key(Key k): _key(k) {}
virtual ~node_key() {}
const Key& key() const { return _key;}
protected:
Key _key;
void _copy_key(node_key<Key> *n) const { n->_key=_key;}
};
template <typename Compare>
struct ptr_less_key {
ptr_less_key() : _comp() {}
virtual ~ptr_less_key() {}
template <typename Pointer>
bool operator()(const Pointer& a, const Pointer& b) const { return _comp(a->key(), b->key()); }
Compare _comp;
};
int main() {
typedef node_key<int>* keyp;
std::multiset<keyp,ptr_less_key<std::less<int>>> x;
node_key<int> k(5);
const node_key<int> *p=&k;
x.insert(p); //this fails
return 0;
}

What you are currently doing: You are not trying to insert a const pointer, as you think you do, but a non-const pointer to a const element.
Change this
const node_key<int> *p=&k;
to this
node_key<int> *const p=&k;
to make the const keyword apply on the pointer rather than on what it points to.

Given
struct node {
void member();
void const_member() const;
};
consider the four declarations
node* pointer_to_node;
const node* pointer_to_const_node;
node* const const_pointer_to_node;
const node* const const_pointer_to_const_node;
There are two different aspects of constness: that of the object node and that of the pointer. The first two declare mutable pointers to either node or const node. A conversion from node* to const node* is allowed (and implicit), but not the other way around, as this would allow to modify a const node.
The second two declarations declare the respective pointers to be constant, i.e. these pointers cannot be modified (though the node pointed to by const_pointer_to_node can.
pointer_to_node->member(); // okay
pointer_to_node->const_member(); // okay
pointer_to_node = new node; // okay
pointer_to_node = const_pointer_to_node; // okay
pointer_to_node = pointer_to_const_node; // ERROR
pointer_to_const_node->member(); // ERROR
pointer_to_const_node->const_member(); // okay
pointer_to_const_node = new node; // okay
pointer_to_const_node = pointer_to_node; // okay
pointer_to_const_node = const_pointer_to_node; // okay
pointer_to_const_node = const_pointer_to_const_node; // okay
const_pointer_to_node->member(); // okay
const_pointer_to_node->const_member(); // okay
const_pointer_to_node = new node; // ERROR
const_pointer_to_node = const_pointer_to_node; // ERROR
const_pointer_to_node = pointer_to_const_node; // ERROR
const_pointer_to_const_node->member(); // ERROR
const_pointer_to_const_node->const_member(); // okay
const_pointer_to_const_node = new node; // ERROR
const_pointer_to_const_node = const_pointer_to_node; // ERROR
const_pointer_to_const_node = pointer_to_const_node; // ERROR

Related

C++ can a reference of pointer take null?

Given the following code:
template<class S, class T>
class node {
public:
S key;
T value;
node<S, T> *right_node, *left_node, *parent_node, *nearest_right_node, *nearest_left_node;
int height;
public:
template<typename, typename> friend
class avl_tree;
node() = default;
node(const S *key, const T *value, node<S, T> *right_node = nullptr, node<S, T> *left_node = nullptr,
node<S, T> *parent_node = nullptr, node<S, T> *nearest_right_node = nullptr,
node<S, T> *nearest_left_node = nullptr) : key(*key),
value(*value),
right_node(right_node),
left_node(left_node),
parent_node(parent_node),
nearest_right_node(nearest_right_node),
nearest_left_node(nearest_left_node),
height(0) {
}
~node() = default;
};
Can I replace the pointers in the c'tor for value and key with references and still able to give key and value a value of nullptr? (for example if S is int*)?
Update:
int* x;
void update_x(int &new_x)
{
x=new_x;
}
can I call update_x(null_ptr)
Your node class has members S key and T value, and constructor parameters const S *key and const T *value which initialize the members using key(*key) and value(*value), respectively, which will copy the input values into the members.
If a nullptr is passed in to either parameter, the code has undefined behavior when dereferencing the nullptr.
There is no reason for the parameters to be pointers at all. They should be passed in either by value:
template<class S, class T>
class node {
public:
S key;
T value;
...
public:
...
node(S key, T value, ...) : key(key), value(value), ... { }
// or: node(S key, T value, ...) : key(std::move(key)), value(std::move(value)), ... { }
...
};
Or by const reference:
template<class S, class T>
class node {
public:
S key;
T value;
...
public:
...
node(const S& key, const T& value, ...) : key(key), value(value), ... { }
...
};
Or by rvalue reference:
template<class S, class T>
class node {
public:
S key;
T value;
...
public:
...
node(S&& key, T&& value, ...) : key(std::move(key)), value(std::move(value)), ... { }
...
};
If S or T are defined as pointer types, then a nullptr can be passed in as the value to be assigned to the key/value members, eg:
node<int, type*> *n = new node<int, type*>(1, nullptr);
type t;
node<int, type*> *n = new node<int, type*>(1, &t);
But if S and T are not pointer types, they should not be passed in as pointers:
type t;
node<int, type> *n = new node<int, type>(1, t);
type t;
node<int, type> *n = new node<int, type>(1, std::move(t));
node<int, type> *n = new node<int, type>(1, type{});
The issue in your code boils down to:
void foo(int* x) {
int y = *x;
}
Passing a nullptr to foo will cause undefined behavior because you shall not dereference a nullptr.
Using a reference is possible but will not change that fact:
void bar(int*& x) {
int y = *x;
}
This is passing the pointer by reference.
int a;
int* p = &a;
bar(p);
Now x in bar is a reference to the pointer to a. However, you cannot pass a nullpointer to bar, because bar is dereferencing the pointer. Some malicious code could do this:
int* p = nullptr;
bar(p); // DO NOT DO THIS !!!
And then int y = *x; will invoke undefined behavior.
If the function expect to always get a valid object it should take it by reference:
void foo_bar(int& x) {
int y = x; // no danger, all fine
}
References cannot refer to nothing. They must be initialzed:
int& x; // ERROR !
int y;
int& z = y; // OK, z refers to y
Pointers on the other hand not always point to a valid pointee:
int* p; // not initialized, no (big) problem
p = nullptr; // still not pointing to an int
int q;
p = &q; // now p points to an int
References cannot refer to nothing, thats why when "nothing" is not a valid parameter you should prefer references over pointers. In your case you always require *value to be valid, hence value always must point to a S and you should use a reference instead to avoid the above mentioned problem.
Concerning your update:
int* x;
void update_x(int &new_x)
{
x=new_x;
}
can I call update_x(null_ptr)
No. You cannot. nullptr is not an int. A reference to an int always references an int.

Returning a non-const reference causes a binding reference error

I've implemented a double linked list using weak and smart pointers. The program is working but I have doubts about that const in the getPrev signature method. If I put const a the end of the method signature it will cause a binding reference error
error: binding reference of type 'std::weak_ptr<Node<Integer> >&' to 'const std::weak_ptr<Node<Integer> >' discards qualifiers
return prev;
Wasn't the purpose of that const to mark *this as const ? The return type is non-const for my understanding.
Here is the code, main.cpp:
#include <memory>
#include <iostream>
#include "DoubleLinkedList.h"
class Integer {
private:
int number;
public:
Integer(int number) : number(number) {}
int get() { return number; }
};
int main() {
DoubleLinkedList<Integer> list;
list.insert(Integer(1));
list.insert(Integer(2));
list.insert(Integer(3));
list.insert(Integer(4));
list.insert(Integer(5));
return 0;
}
DoubleLinkedList.h
#include <memory>
#include <vector>
#include <iostream>
template <typename T>
class Node {
private:
T data;
std::weak_ptr<Node> prev;
std::shared_ptr<Node> next;
public:
Node(): data(0) {}
Node(const T &object) : data(object) {};
T getData() const {
return data;
}
void setData(T data) {
Node::data = data;
}
std::weak_ptr<Node> &getPrev() const {
return prev;
}
void setPrev(const std::weak_ptr<Node> &prev) {
Node::prev = prev;
}
std::shared_ptr<Node> &getNext() {
return next;
}
void setNext(const std::shared_ptr<Node> &next) {
Node::next = next;
}
};
template <typename T>
class DoubleLinkedList {
private:
std::shared_ptr<Node<T>> header;
std::weak_ptr<Node<T>> trailer;
int size;
public:
DoubleLinkedList() : size(0) {}
void insert(const T &value) {
auto node = std::make_shared<Node<T>>(value);
if (size++ == 0) {
header = node;
} else {
auto last = trailer.lock();
last->getNext() = node;
node->getPrev() = last;
}
trailer = node;
}
};
If you are inside a const method, all the data members are considered const.
That is, inside this function:
std::weak_ptr<Node> &getPrev() const
you can imagine the member variables like this:
const T data;
const std::weak_ptr<Node> prev;
const std::shared_ptr<Node> next;
It should be clear that you cannot return a non-const reference to a const object:
const int x;
int& getX()
{
return x; // error
}
The reference would allow you to modify x even though it is const, so this is forbidden (formally: a non-const reference cannot bind to a const object).
Inside a const member function of Node, prev is a const std::weak_ptr<Node>, so a std::weak_ptr<Node>& cannot bind to it for the exact same reason.
It appears that within insert you do intend to modify node (by changing its prev value), in which case the getPrev function should not be const (because you intend to modify the object). But this kind of access should probably be reserved for the DoubleLinkedList and not some arbitrary outside user. It then becomes a question of interface design: Which parts of your code are implementation details and how those should be hidden from users? Which parts are the interface that users should interact with (with minimal opportunity for breaking things)?

C++ How to initialize a struct through no argument constructor

Node is a struct which defined in a class List_set private.
struct List_set::Node
{
element_t str_l;
link_t next_ptr ;
static link_t* find(link_t* current, element_t string_t);
Node();
};
List_set::List_set():link_(nullptr)
{}
List_set::Node::Node():next_ptr{0}
{}
I want to use a constructor to initialize the Node, however the compiler always report an error:
no matching constructor for initialization of 'list_set::List_set::Node'
Can you please help to figure it out?
The next is the definition of class List_set hope it can provide some reference.
class List_set
{
public:
using element_t = std::string;
List_set();
List_set(std::initializer_list<element_t>);
bool is_empty() const;
size_t size() const;
bool contains(const element_t&) const;
void insert(element_t);
private:
struct Node;
using link_t = std::shared_ptr<Node>;
link_t link_;
};

const and rvalue references

Im trying to write an efficient list and node classes that have minimal code to iterate over them. However I'm having difficulty fulfilling all my criteria. I want this code to be used safely in a multi-threaded environment and so if possible I want all the functions and arguments for iteration to be const to ensure no state is being written to ie its all read only. So I wrote the below classes and iteration code:
template<typename _Type_>
class Node
{
template<typename _Type_> friend class List;
public:
Node() : m_Next(NULL) {}
Node(const _Type_& value) : m_Value(value), m_Next(nullptr()) {}
const _Type_& Get() const { return m_Value; }
Node&& Next() const { return std::move(*m_Next); }
operator _Type_ () const { return m_Value; }
_Type_& operator->() { return m_Value; }
const _Type_& operator->() const { return m_Value; }
operator bool() const { return m_Next != nullptr(); }
private:
_Type_ m_Value;
Node* m_Next;
};
template<typename _Type_>
class List
{
public:
typedef Node<_Type_> Node;
List() : m_Head(NULL) {}
void AddHead( const _Type_& value )
{
Node* pNode = GE_NEW(Node, value);
pNode->m_Next = &m_Head;
m_Head = *pNode;
}
Node&& Head() const { return std::move(m_Head); }
private:
Node m_Head;
};
and the all important iteration code:
RenderTargetList::Node&& renderTarget = m_Targets.Head();
while( renderTarget )
{
if(renderTarget->Format() == format)
{
return renderTarget.Get();
}
renderTarget = renderTarget.Next();
}
But this doesn't compile as:
Node&& Head() const { return std::move(m_Head); }
returns a non const rvalue reference in a const function function ie it has to be:
const Node&& Head() const { return std::move(m_Head); }
instead, but then this doesn't work as the iteration code fails on the assignment:
renderTarget = renderTarget.Next();
because renderTarget must now be defined as:
const RenderTargetList::Node&& renderTarget = m_Targets.Head();
because head returns a const. Basically I seem to be in a right mess of const, references, lvalues and rvalues. Someone please help!
Thanks
Here's the basic version of a list class. Note that the iterator type and the node type are two distinct types. This way, the node type can own the value, and the iterator type can have pointer semantics.
I'll post this as a community wiki, as it's rather a comment than a direct answer to the question.
template<typename Type>
class List
{
private:
// note that for immutable nodes, we could store `Type const`
// and for an immutable list, `Node const* const`
struct Node
{
Type m_Value;
Node* m_pNext;
};
Node* m_pHead;
public:
class const_iterator
{
private:
Node const* m_pNode;
friend List;
const_iterator(Node* p_pNode) : m_pNode(p_pNode) {}
public:
const_iterator() : m_pNode(nullptr) {}
explicit operator bool() const
{ return m_pNode; }
const_iterator Next() const
{ return {m_pNode->m_pNext}; }
Type const& Get() const
{ return m_pNode->m_Value; }
friend bool operator!=(const_iterator const& lhs,
const_iterator const& rhs)
{
return lhs.m_pNode != rhs.m_pNode;
}
};
List() : m_pHead(nullptr) {}
~List()
{
// delete nodes
}
List(List const&) = delete; // needs to be customized
List& operator=(List const&) = delete; // this one, too
// the only function that modifies the list:
void AddHead( Type const& value )
{
Node* pNode = new Node{value, m_pHead};
m_pHead = pNode;
}
const_iterator Head() const
{ return {m_pHead}; }
};
Usage example:
#include <iostream>
int main()
{
List<int> l;
for(int i = 0; i < 10; ++i) l.AddHead(i);
auto it = l.Head();
while(it)
{
std::cout << it.Get() << ", ";
it = it.Next();
}
}
I think you are confused at to the point of const if your goal is thread safety -- const does not give you thread safe code, only considerate and well understood access patterns will give you thread safe code.
I have lost of code which is thread sfae which does not make use of const, and you will need to as well if you want to be able to keep the "AddHead" method which certainly in its current form is not thread safe.
If your code is threaded you will need to add mutex locks around the code where you are accessing shared data which could be modified or read by another thread -- the critical zones -- from your code structure these critical zones could be very short lived, so not much of a penalty.

how to preserve const correctness across pointers?

I am trying to have a const operation on a class that is truly const - it does not change data that the class points to.
For example:
class Node{
public:
int val;
};
class V{
public:
Node * node; //what is the change that is needed here?
void const_action()const{
node->val=5; //error wanted here
}
void action(){
node->val=5; //error is not wanted here
}
};
You can use a template to enforce the const correctness on a pointer without changing the meaning or the implementation of your class:
template <typename T>
class PreseveConstPointer
{
T *t_;
public:
PreseveConstPointer(T *t = nullptr)
: t_(t)
{
}
PreseveConstPointer<T> * operator=(T *t)
{
t_ = t;
return this;
}
T* operator->()
{
return t_;
}
T const * operator->() const
{
return t_;
}
T * data()
{
return t_;
}
};
class Node{
public:
int val;
};
class V{
public:
PreseveConstPointer<Node> node;
V()
{
node = new Node;
}
~V()
{
if(node.data())
delete node.data();
}
void const_action()const{
node->val=5; // You will get an error here
}
void action(){
node->val=5; // No error here
}
};
const after a function declaration says that the function is not allowed to change any class members (except ones that are marked mutable).
Since your code doesn't change any class member, and only changes the object node points to, both function will compile.
AFAIK there's no way to prevent this. If you mark the node const, neither will compile.
You're confusing Node* const for Node const*.
An [unfortunate?] side effect of using indirection here is that constness of the pointer member has nothing to do with the actual Node on which you're operating.
If you don't need that member to be a pointer, then this is pleasingly easy:
class V
{
public:
Node node;
void const_action() const
{
node.val = 5; // error here
}
void action()
{
node.val = 5; // no error here
}
};
However, given its name, I suspect life is not that simple and you are basically out of luck.