deep_const_ptr copy constructor - c++

template <class T>
class deep_const_ptr
{
T * priv;
public:
deep_const_ptr(const deep_const_ptr & p_other); // copy ctor
T const * operator->() const;
T * operator->();
..... // other crap
};
void Cheese::doThings(const deep_const_ptr<Cheese> p_other)
{
deep_const_ptr<Cheese> awayGoesTheConst(p_other); // I don't want this to work.
awayGoesTheConst->doTerribleThings();
}
I'd like to block copy construction from a const to a non-const.
Maybe this doesn't even make any sense?
Might this be possible with the new C++11 / 14 / 17?
EDIT:
Yes, the issue is that if I don't give the copy constructor, STL containers wont work. And I'd quite need them to work.
EDIT:
deep_const_ptr was the solution what I came up with when faced with the problem of STL containers and const objects.
class Shape
{
private:
std::list<Vertex *> m_vertices;
public:
virtual void std::list<Vertex*> * const getVertices() { return &m_vertices; }
virtual void std::list<Vertex const *> * const getVertices() const { return &m_vertices; } // doesn't work, cannot convert between different types of lists
}
I want to provide Immutable Vertex objects from an Immutable Shape.
The two solutions I came up with were custom deep_const_ptr pointer types and custom lists where
Vertex const *& operator[](unsigned int) const;

I assume what you are after is a deep-const-pointer implementation and what to do with its copying. As you already found out, a simple copy constructor will not do, since you will just lose the const on your pointer. There is no other way to disallow the const removal on the outer object, so you just need to disable the copy constructor.
Now the current version of your question does not make it clear why that is a problem. From your comments and the previous question, I can only infer that you want to use the pointer in containers, std::vector<> specifically. The key to making that work is to disable copy, but enable move (Note that you need rvalue-reference support for this, which is part of C++11):
template <class T>
class deep_const_ptr
{
T * priv;
public:
deep_const_ptr(const deep_const_ptr &) = delete; // Do not allow this!
deep_const_ptr& operator=(const deep_const_ptr &) = delete; // Nor this!
deep_const_ptr(deep_const_ptr &&) = default; // But this is okay
deep_const_ptr& operator=(deep_const_ptr &&) = default; // So is this!
T const * operator->() const;
T * operator->();
..... // other crap
};
Containers typically only need move, not copy, to work.
If you really need support for the copy, you need to do it to a different type that only supports the const access. For example, you could specialize the deep_const_ptr for const T and only implement the const variant of operator-> and operator* in there. Additionally, you'd allow "demotion" of a deep_const_ptr to a deep_const_ptr via the conversion operator. But this implies some sort of multiple owner handling, such as reference counting, so I'm going to leave it out. It's also weird that the const and non-const version behave differently, but it's your decision. Personally, I'd recommend just sticking with the move.

Edit Edit: You updated your answer again, so let me reply to that.
Just use a const pointer:
void Cheese::doThings(const Cheese* p_other)
{
Cheese* p = p_other; // compiler ERROR
p->doTerribleThings(); // can not do terrible things
}
Or if you want a shared_ptr:
void Cheese::doThings(const std::shared_ptr<const Cheese>& p_other)
{
std::shared_ptr<Cheese> p = p_other; // compiler ERROR
p->doTerribleThings(); // can not do terrible things
}
const ptr<T> is not the same as ptr<const T>. The first indicates a pointer object which can not be changed, while the object pointed to can. The second indicates a pointer object which can be changed, but the object pointed to can not.
For a function signature void foo(const A a) does not make much sense. You say "give me a copy, and I promise to not change it". Who cares what you do with your copy? Use void foo(const A& a) to avoid creating a copy.
Edit:
I'd like to allow copying nonconst -> nonconst, nonconst -> const,
const -> const.
This is already given for a "normal" pointer T* or an std::shared_ptr.
Old:
Just use T* operator->() const;. If the stored pointer will be a non-const pointer like Cheese* it can be converted to a const Cheese* automatically.
The function operator->() is marked as const, as it does not change the pointer object itself. This has nothing to do with the stored object!
Example with vector:
std::vector<Cheese*> a(10);
a[0] = 0; // does change a
Cheese* p = a[0]; // does not change a
a[0]->foo(); // still does not change a!
If you want objects pointed to by deep_const_ptr to be const, you have to use deep_const_ptr<const Cheese>.
Example with vector:
std::vector<const Cheese*> a(10); // const pointer!
a[0] = 0; // does change a
const Cheese* p = a[0]; // does not change a
a[0]->foo(); // now this only works if Cheese::foo is const

Related

How to make a deep-const pointer

Let's say I want to represent a binary tree in C++. Usually, I want a Node struct like this:
struct Node {
Node* left
Node* right;
};
(Here I use struct and raw pointers just for simplicity. I know I should use smart pointers for memory management.)
This representation has a problem: I can never have a deep-const tree. (Correct me if I can.) I may mark a single Node const, but its children is hard-coded as non-const in the Node struct.
(I may use some template hack to make left and right optionally const, but this makes the const Node and non-const Node incompatible.)
Soon I found out, if I magically had some deep-const pointer (say deep_const_pointer, which makes constness transitive), I can use that pointer in Node so that having a const node automatically means having a const sub-tree.
I tried to write a deep-const pointer class, and here is what I end up with:
template <typename T>
class deep_const_pointer {
public:
explicit deep_const_pointer(const T* p_)
: p{const_cast<T*>(p_)} {}
const T& operator*() const { return *p; }
T& operator*() { return *p; }
const T* operator->() const { return p; }
T* operator->() { return p; }
// etc.
private:
T* p;
};
Here I cast out the const in the constructor and optionally add it back according to the constness of this pointer-like object. However, this implementation allows the following:
const int i = 42;
deep_const_pointer<int> p{&i};
*p = 0; // Oops!
So it depends on the user to correctly mark whether the pointer is const or not.
How should I build a deep-const pointer class? Ideally, I want the const check happen at compile-time, and that pointer class takes as much memory as a raw pointer. (Which rules out the solution to save the constness to a bool member variable and check on each access.)
EDIT: I checked std::experimental::propagate_const, and it is indeed not a "deep-const" pointer from my perspective. What I meant by deep-const pointer P is:
Constant P is pointer-to-const;
Mutable P is pointer-to-mutable;
A const reference to a non-const P is treated as if it were a const P;
Since pointer-to-const has value semantics, a const P should be trivially copyable.
propagate_const fails to match the requirement because:
It never accepts a pointer-to-const;
It is not copyable (copy constructors explicitly deleted);
From the comments and answer I received, I guess such a P is not implementable in C++.
Writing a transitive-const smart pointer is a solved problem, just look up std::experimental::propagate_const<>. It shouldn't be too hard to find appropriate implementations.
In your own try, you got constructing from a raw pointer wrong. You should not add const to the pointee-type, nor strip it out with a cast.
Fixed:
explicit deep_const_pointer(T* p_)
: p{p_} {}

Differentiating between const references to immutable vs. mutable objects

Is there any accepted way in C++ to differentiate between const references to immutable objects vs. mutable ones?
e.g.
class DataBuffer {
// ...
};
class Params {
// ...
};
class C {
public:
// Given references must be valid during instance lifetime.
C(const Params& immutableParameters, const DataBuffer& mutableDataBuffer) :
m_immutableParameters{immutableParameters},
m_mutableDataBuffer{mutableDataBuffer}
{
}
void processBuffer();
private:
const Params& m_immutableParameters;
const DataBuffer& m_mutableDataBuffer;
};
Here the semantic difference is given just in the names.
The problem is that const& instance variables only let you know the object won't be modified by the instance. There is no distinction in the interface whether or not they may be modified elsewhere, which I think is a useful feature to be able to describe in the interface.
Expressing this through the type-system would help make interfaces clearer, allow the compiler to catch errors (e.g. accidentally modifying parameters handed to a C instance, outside of the instance, in the example above), and possibly help with compiler optimizations.
Assuming that the answer is that the distinction isn't possible in C++, maybe there is something close which can be achieved with some templates magic?
Immutability is not part of the C++ type system. As such, you cannot differentiate between immutable objects and mutable ones. And even if you could, std::as_const will always ruin your attempt to do so.
If you are writing an interface that requires immutability of objects, the easiest way to handle this is to invoke the Fundamental Theorem of Software Engineering: "We can solve any problem by introducing an extra level of indirection." So make immutability part of the type system. For example (FYI: uses some small C++17 library stuff):
template<typename T>
class immutable
{
public:
template<typename ...Args>
immutable(std::in_place_t, Args &&...args) t(std::forward<Args>(args)...) {}
immutable() = default;
~immutable() = default;
immutable(const immutable &) = default;
//Not moveable.
immutable(immutable &&) = delete;
//Not assignable.
immutable operator=(const immutable &) = delete;
immutable operator=(immutable &&) = delete;
const T* operator->() const {return &t;}
const T& operator*() const {return t;}
private:
const T t;
};
With this type, the internal T will be immutable regardless of how the user declares their immutable<T>. Your C class should now take an immutable<Params> by const&. And since immutable<T> cannot be constructed from a copy or move of an existing T, the user is forced to use immutable<Params> whenever they want to pass that as a parameter.
Of course, your biggest danger is that they'll pass a temporary. But that was a problem you already needed to solve.
I don't know the reason, but here's how you can do it:
struct C {
template<typename T, typename T2>
C(T&&, const T2&&) = delete;
C(const Params&, const DataBuffer&) { /*...*/ }
};
By declaring a constructor that takes any argument by non-const reference, it will always be a better match than the constructor taking const&, as a cv-qualifier doesn't have to be added.
The const& constructor is a better match when passing a const parameters, as the cv-qualifier doesn't have to be removed.
DataBuffer db;
const Params cp;
C c{ cp, db }; // ok, second constructor call is chosen
Params p;
C c2{ p, db }; // error, constructor is deleted
Due note that, as #IgorTandetnik said, you can break your requirement easily:
Params pa;
const Params& ref_pa = pa;
C c3{ ref_pa, db }; // ok, but shouldn't compile.
As previous answers, C++ doesn't have the concept of "immutable". #Rakete1111 gave you the answer I would have used. However, Visual Studio will put global const variable in .rdata segment, where other variables will go to .data. The .rdata segment will generate a fault when trying to write.
If you need a run time test whether an object is read only, use a signal handler, like this:
#include <csignal>
const int l_ci = 42;
int l_i = 43;
class AV {};
void segv_handler(int signal) {
throw AV{};
}
template <typename T>
bool is_mutable(const T& t)
{
T* pt = const_cast<int*>(&t);
try {
*pt = T();
}
catch (AV av) {
return false;
}
return true;
}
void test_const()
{
auto prev_handler = std::signal(SIGSEGV, segv_handler);
is_mutable(l_i);
is_mutable(l_ci);
}
What you need is not a const reference, but a const object. Value semantics solve your problem. Nobody can modify a const object. While a reference is only const where it is marked const, because the referenced object may not be const. Take that for example :
int a;
int const& b = a;
// b = 4; <-- not compiling, the reference is const
Above, a is int, and b is a reference to const int. While a is not const, the language permit the reference to const to be bound on a non const object. So it's a reference to const object that is bound to a mutable object. The type system won't allow you to modify the mutable object through the reference, because it may have been bound to a const object. In our case it isn't, but the tribe don't change. However, even declaration of a reference to const won't change the original declaration. The int a is still a mutable object. a may still change value:
a = 7;
This is valid, whatever references or other kind of variables have been declared. A variable declared as int (no const) can change, and nothing can prevent it from changing. Heck, even another program like cheat engine can change the value of a mutable variable. Even if you had rules in the language to guarantee that it won't be modified, there is nothing they will prevent the mutable variable from changing values. In any language. In machine language, a mutable value is permitted to change. However, maybe some API of the operating system can help you change the mutability of memory regions.
What can you do to solve this problem now?
If you want to be 100% sure an object won't be modified, you must have immutable data. You usually declare immutable objects with the const keyword :
const int a = 8;
int const& b = a;
// a cannot change, and b is guaranteed to be equal to 8 at this point.
If you don't want a to be immutable and still guarantee b to not change, use values instead of references :
int a = 8;
const int b = a;
a = 9;
// The value of b is still 8, and is guaranteed to not change.
Here, value sematic can help you have what you want.
Then const reference are there for what? There are there to express what you are going to do with the reference, and help enforce what can change where.
As the question has been further clarified, no there is no way to determine if the reference has been bound to a mutable or immutable object in the first place. There is, however, some tricks you can have to differentiate the mutability.
You see, if you want more information about the mutability to be passed along with the instance, you can store that information in the type.
template<typename T, bool mut>
struct maybe_immutable : T {
using T::T;
static constexpr auto mutable = mut;
};
// v--- you must sync them --v
const maybe_immutable<int, false> obj;
This is the most simple way to implement it, but a naive one too. The contained data will be conditionally immutable, but it forces you to sync template parameter and constness. However, the solution allows you to do this :
template<typename T>
void do_something(const T& object) {
if(object.mutable) {
// initially mutable
} else {
// initially const
}
}
I hope I understand you question correct it is not as explicit as so to say "D language" but with const r-value references you can make immutable parameters.
What I understand from immutable is forexample
void foo ( const int&& immutableVar );
foo(4);-> is ok
int a = 5;
foo(a);->is not ok

Can an rvalue be moved into a shared_ptr

Is it possible to 'move' an rvalue into a shared_ptr. All the methods which I've tried so far result in a copy.
My desired usage pattern is:
class Element {
public:
Element(const string &);
Element(const Element &) = delete; //delete copy constructor
// ...
};
class MyCollectionOfElements {
public:
void add(Element &&); // add by rvalue
// ...
protected:
vector<shared_ptr<Element>> elements;
};
MyCollectionOfElements elements;
elements.add(Element("something"));
There are reasons why I want this pattern, and understand that there are alternative patterns which naturally work (e.g. passing the new Element as a pointer rather than an rvalue).
My current suspicion is that the incoming rvalue is on the stack, whilst I need to have it on the heap to store it in a shared_ptr, therefore a copy is inevitable.
Yes:
void add(Element && e)
{
elements.push_back(std::make_shared<Element>(std::move(e)));
}
You also have to make sure your class actually has a move constructor:
class Element
{
public:
Element(Element &&) = default;
// ...
};
By declaring a copy constructor, you inhibit the implicit declaration of a move constructor, so you need to declare it explicitly.
Guess you misunderstand the idea of movement semantic. You have to pass pointer to smart pointer, and pointer can have any type - lvalue/rvalue
void f(A *&&p) {}
function which accept rvalue ref to pointer which points to A; but p is still lvalue which has type r-value reference to a pointer,
so u have to "cast"(std::move - does nothing just cast l-value to r-value)
std::shared_ptr(std::move(p));
A *g() { return new A;}
A *a;
f(a);//error
f(g());//works fine
f(new A());//also fine

Preserve constness on assigning an object

Is it possible to preserve constness of an object when assigning? Consider a class holding pointer to dynamic memory, a soft copy is expected on assignment. (There is some more complex logic including reference counting, but it is not relevant for the question).
class Vec {
private:
int *data_; // dynamic array
public:
int * Data() { return data_;} // access for modification
const int * Data() const { return data_;} // read only access
Vec subVec(int pos) { // create writable submat
Vec ret;
ret.data_ = &data_[pos];
return ret;
}
const Vec subVec(int pos) const { // create read only submat
Vec ret;
ret.data_ = &data_[pos];
return ret;
}
};
void processVec(const Vec & src) {
src.Data()[0] = 1; // not allowed, since src const
Vec subVec = src.subVec(2); // call const version of subVec and create
// a soft copy - wrapper for a part of array.
// Assignment creates a copy, which removes constness
subVec.Data()[0] = 1; // allowed! Since subVec is a copy, but modifies
// identical dynamic memory from the wrapped src!
}
I would like subVec.Data()[0] = 1; to fail, since it is supposed to remain const.
Your problem is, that you are defining a smart pointer to some object, but you confuse constness of the smart pointer (the analogue to Foo* const bar;) with constness of the object (the analogue to const Foo* bar;). What you need, is to separate the constness of the pointer from the constness of the object.
You can relatively easily do so by using two smart pointer classes instead of one: A base class, that implements the smart pointer for a const object, and a derived class that implements the smart pointer for a non-const object.
With that, you can always demote your smart pointers to the "const" base class, make copy free "const" copies, etc. You might even add a constructor to the derived, non-"const" smart pointer class that takes a smart pointer of the "const" base class to make a deep copy of the data.
I would mimic the iterator and const_iterator idiom by creating a different return type when working with const or non-const object. Here an example implementing just a different type for const call to subVec:
class Vec {
// ...
public:
// ...
const int * Data() const { return data_;} // read only access
Vec subVec(int pos) { // create writable submat
Vec ret;
ret.data_ = &data_[pos];
return ret;
}
class SubVec
{
private:
const int* data_;
public:
SubVec(const int* data) : data_(data) {}
const int * Data() const { return data_;} // read only access
};
const SubVec subVec(int pos) const { // create read only submat
SubVec ret(&data_[pos]);
return ret;
}
};
This will generate the error you want in the const case and will compile in the non const case. Using auto allows a easier code writing for your code user.
IIUC, the elements which are either const or non-const are assigned at run-time, hence the type system (which is static) can not help you with that. You'd need to store some boolean flag with each element and perform a run-time check on assignment to see if it allowed or not.

C++: implementing a smart pointer

I am reading through implementing smart pointers and I found the following code,
template <class T>
class SmartPtr
{
public:
explicit SmartPtr(T* pointee) : pointee_(pointee);
SmartPtr& operator=(const SmartPtr& other);
~SmartPtr();
T& operator*() const
{
...
return *pointee_;
}
T* operator->() const
{
...
return pointee_;
}
private:
T* pointee_;
...
};
I am not able to understand the following,
"SmartPtr& operator=(const SmartPtr& other)": why the parameter is constant? Doesn't it lose its ownership when the assignment is done?
And why do we need "T& operator*() const" and "T* operator->() const" methods?
Thx#
Point 1. Not necessarily, depends on the design of the smart pointer. Some like boost:shared_ptr do not transfer ownership on assignment.
Point 2. Those methods simulate normal pointer operations on the smart pointer.
To answer 2.:
To simulate a raw pointer. You can use *ptr to return the object it points to (just like a C-pointer), and you can use ptr->foo() to call the method foo in T, (just like a C-pointer).
There are two types of smart pointer semantics I can think of that would work with that signature. The first would be a reference counting pointer like std::shared_ptr. The other would be value semantics, i.e. copying the pointer makes a new copy of the pointed to object. This signature wouldn't work with a pointer like auto_ptr/unique_ptr.