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.");
};

      

+3


source to share





All Articles