Let's say we have a class X
that holds a pointer to an object of class Y
. X
never changes Y
in any way, but other objects which might want to change Y
can ask X
for a pointer to Y
. We want class X
to be able to hold both const
and variable objects. If we write something like this:
class Y;
class X {
public:
const Y* getY();
private:
const Y* y;
};
then we could never alter Y
when getting it from X
, even when the original "Y
object" is not const
.
An example where this would be useful is a linked list that holds both const
and variable objects.
How would one go about implementing this?
You can think about the analogous situation of std::unique_ptr
: one that holds a pointer to a mutable Y
is std::unique_ptr<Y>
, while one that holds a pointer to a const
Y
is std::unique_ptr<const Y>
.
X
can similarly be made into a template:
class Y;
template <class T>
class X {
public:
T* getY();
private:
T* p;
};
Here, X<Y>
can hold a Y*
, and X<const Y>
can hold a const Y*
. You may also want to make X<Y>
implicitly convertible to X<const Y>
by providing an appropriate constructor, so that any function that has a parameter of type X<const Y>
can be called with an argument of type X<Y>
:
template <class T>
class X {
public:
X(const X& other) = default;
X(const X<std::remove_const_t<T>>& other)
requires (!std::is_const_v<T>)
: p(other.getY()) {}
T* getY() const;
private:
T* p;
};
If you want to hide the templatedness from the users, you can do so like this:
namespace detail {
template <class T>
class X_impl {
public:
X_impl(const X_impl& other) = default;
X_impl(const X_impl<std::remove_const_t<T>>& other)
requires (!std::is_const_v<T>)
: p(other.getY()) {}
T* getY() const;
private:
T* p;
};
} // namespace detail
using X = X_impl<Y>;
using CX = X_impl<const Y>;