How to ensure that the object is actually inherited?
Is it possible to define a base class for which it can only be practically inherited? That is, we can fill the points in
struct Base {
static_assert (..., "You may only inherit virtually from Base");
};
such that
struct A : public virtual Base {};
compiles fine, but
struct B : public Base {};
not? Of course, Base
it cannot know all the types that inherit from it, but we can naively try to use CRTP here:
#include <type_traits>
template <typename taBase, typename taDerived>
struct VirtualBase : public taDerived, public virtual taBase {};
template <typename taDerived>
struct NonVirtualBase : public taDerived {};
template <typename T, typename U>
struct IsVirtualBaseOf : std::integral_constant <bool,
std::is_base_of <T, U>::value &&
sizeof(NonVirtualBase <U>) == sizeof (VirtualBase <T, U>)
> {};
template <typename taDerived>
struct Base {
static_assert (IsVirtualBaseOf <Base <taDerived>, taDerived>::value, "");
};
struct A : public virtual Base <A> {};
struct B : public Base <B> {};
It won't work because when Base
created, A <Base>
and B <Base>
are still incomplete types, and inheritance from them in VirtualBase
and NonVirtualBase
therefore results in a compiler error. (Also std::is_base_of
won't work for the same reason)
Of course, we can move static_assert
to A
and B
, but since they resemble client classes to be defined, I would like to provide virtual inheritance by definition Base
. Perhaps there is a way to defer validation until A
and are B
no longer incomplete by wrapping, Base
or perhaps this can be achieved using C ++ concepts. Any suggestions are greatly appreciated. Thanks in advance.
Side note: The ability for forwarding to declare inheritance intent on an object without defining it would now be highly desirable, wouldn't you agree? For example.
struct X {};
struct Y : public X;
struct Y : public X {};
Edit:
Even though this question is indeed a duplicate, I believe it might come up with a bit of a solution that works for my use case. This is not the answer to this question. As noted in the peterchen comment section, the inheritance hierarchy is known to have additional derived classes and this is critical to solving the problem in my situation.
First, let me explain the use case a little further. Base
usually struct
carries the concept of NonConstructible, inheriting from the base Traits
.
struct Traits {
private:
Traits ();
};
The desire for uniformity arises because we would like all feature classes to be non-constructive but still definable types. To this end, we may agree to inherit from Traits
. Different use cases may require overriding (by hiding) different elements of a feature class Base
. For example,
struct Base {
typedef int SocketIdType;
typedef short PortType;
typedef char IpAddressType[4];
};
struct SpecialSocketIdTraits : public Base {
typedef unsigned int SocketIdType;
};
struct SpecialPortTraits : public Base {
typedef unsigned short PortType;
};
template <typename> struct Socket;
typedef Socket <SpecialSocketIdTraits> Socket1;
typedef Socket <SpecialPortTraits> Socket2;
It is often necessary to combine these independent trait override classes. Therefore, we introduce the MergeTraits
struct.
template <typename... taTraits>
struct MergeTraits : public taTraits... {};
We would like this to allow us to do the following in general:
typedef MergeTraits <SpecialPortTraits, SpecialSocketIdTraits> SpecialSocketIdAndPortTraits;
typedef Socket <SpecialSocketIdAndPortTraits> Socket3;
However, since both classes of characteristics have ::SocketIdType
, for example, the search is ambiguous. Looking at the situation, you can say that virtual inheritance through a hierarchy by default would allow ambiguities, but would also satisfy your expectations. Since it is Base
inherited only once and SpecialSocketIdTraits
hides ::SocketIdType
from Base
, we guarantee that it SpecialSocketIdAndPortTraits::SocketIdType
is unsigned int
. If there is another such
struct SpecialSocketIdTraits2 : public virtual Base {
typedef long SocketIdType;
};
Then searching ::SocketIdType
through MergeTraits <SpecialSocketIdTraits, SpecialSocketIdTraits2>
would again be ambiguous. Now for a solution inspired by peterchen's comment. We can check the package taTraits
for structures that inherit from certain base traits. These inheritances can then be verified as virtual.
template <typename...> struct Tuple;
template <typename, typename> struct ShouldMaybeVirtualInheritFrom;
template <typename B>
struct ShouldMaybeVirtualInheritFrom <Tuple <>, B> : std::true_type
{};
template <typename D, typename B>
struct ShouldMaybeVirtualInheritFrom <Tuple <D>, B> :
std::integral_constant <bool,
!std::is_base_of <B, D>::value || IsVirtualBaseOf <B, D>::value
>
{};
template <typename D, typename... Ds, typename B>
struct ShouldMaybeVirtualInheritFrom <Tuple <D, Ds...>, B> :
std::integral_constant <bool,
(!std::is_base_of <B, D>::value || IsVirtualBaseOf <B, D>::value) &&
ShouldMaybeVirtualInheritFrom <Tuple <Ds...>, B>::value
>
{};
The real solution in my code is a little more complex and uses some of the already existing template metaprogramming functions as syntactic sugar, but that's what it boils down to. Now we can use this component in ours MergeTraits
to detect virtual inheritance from Base
.
template <typename... taTraits>
struct MergeTraits : public taTraits... {
static_assert (ShouldMaybeVirtualInheritFrom <Tuple <taTraits...>, Base>::value,
"For all traits T in taTraits...: if T inherits from Base it should do so virtually.");
};
The downside is that we need, of course, to pollute with a MergeTraits
list of these statements. However, we can delegate checks to additional derived classes, at least to solve my problem.
template <typename... taTraits>
struct MergeTraits : public taTraits... {};
template <typename... taTraits>
struct MergeFromBase : public MergeTraits <taTraits...> {
static_assert (ShouldMaybeVirtualInheritFrom <Tuple <taTraits...>, Base>::value,
"For all traits T in taTraits...: if T inherits from Base it should do so virtually.");
};
source to share
No one has answered this question yet
See similar questions:
or similar: