Reputation: 132128
Suppose I have the following class:
template<typename T>
struct A { T* ptr; };
and a bunch of functions which take this class as a parameter, e.g.:
template <typename T>
int one_two_three(A<T> a) { return 123; }
Now, I want to define a class which is:
T*
(which one can obtain in an A<T>
), and one can pass it somehow to functions taking A; butA
, e.g. lacks public access to its members etc.Now comes my dilemma. Here are two such A
-like classes, named B
and C
:
template<typename T>
class B {
public:
using a_type = A<T>;
B(T* ptr) : a_{ptr} { }
a_type get() const noexcept { return a_; }
operator a_type() const noexcept { return get(); }
private:
a_type a_;
};
template<typename T>
class C : public A<T> {
public:
using a_type = A<T>;
C(T* ptr) : a_type{ptr} { }
a_type get() const noexcept { return *this; }
operator a_type() const noexcept { return get(); }
};
If I use class B
, I am unable to substitute A's in calls to one_two_three()
.
On the other hand, if I use class C
- I am able to substitute A's in calls to one_two_three()
; but then I am breaking the Liskov substitution principle: C
is a subtype of A
, but one cannot replace an instance of C wherever an instance of A
is used, since C
is more restricted.
Can I somehow achieve both the benefits of B
and of C
in the same class? Or must I choose between these two approaches?
Note:
Motivation: My A
is actually a span class, and B
or C
adds ownership semantics, like a unique_ptr but with a size, and should be usable where I would use a span.
Upvotes: 1
Views: 94