Can it be OK to derive from a standard library type while also violating the Liskov principle?
https://softwareengineering.stackexchange.com/questions/326094
-
23-12-2020 - |
Question
I would like to define a shared pointer whose operator==
compares the pointed to values instead of the pointers, something like this:
template <typename T>
struct deref_shared_ptr: std::shared_ptr<T> {
bool operator==(const deref_shared_ptr rhs) const {
return (**this == *rhs);
}
};
This type has two potential problems:
It derives from a standard library type.
It violates the Liskov substitution principle, since it changes the semantics of
operator==
.
However, I am thinking that neither of the two is a problem unless one uses the base type pointer/reference to refer to the derived type. In particular, these problems might never occur in template code. For shared pointers the situation is even better: shared pointers are stored and passed by value.
Is my logic correct? Are there still good reasons to avoid the above definition in favor of composition?
Solution
I won't say your logic is definitively wrong, but it gives me a queasy feeling -- e.g. what happens if someone accidentally uses it as a shared_ptr
anyway?
In this sort of situation I make my own class that contains the shared_ptr
. E.g.
class CustomPtr {
shared_ptr<XType> m_xp;
public:
... some stuff ....
operator bool()const{return (bool)m_xp;}
void reset() {m_xp.reset();}
Transport *operator->(){return m_xp.get();}
const Transport *operator->()const{return m_xp.get();}
... more stuff ....
};
I have to proxy or otherwise wrap any interface that I want to use -- but I consider this an advantage. I generally only use a handful of access methods, and I like to be confident that no-one is using unexpected methods.