是否可以定义一个只能从虚拟继承的基类?也就是说,我们可以填写
中的点struct Base {
static_assert (..., "You may only inherit virtually from Base");
};
这样
struct A : public virtual Base {};
编译好,但
struct B : public Base {};
没有?当然,Base
可能无法知道将从中继承的所有类型,但我们可能会天真地尝试在这里使用CRTP:
#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> {};
这不起作用,因为在Base
实例化时,A <Base>
和B <Base>
仍然是不完整的类型,并在VirtualBase
和NonVirtualBase
中继承它们产生编译器错误。 (另外,std::is_base_of
由于同样的原因而无法工作)
当然,我们可能会将static_assert
移动到A
和B
,但是因为这些类似于要定义的客户端类,我想通过定义强制执行虚拟继承Base
。也许有一种方法可以通过包装A
来延迟验证,直到B
和Base
不再是不完整的类型,或者这可能是使用C ++概念实现的。欢迎任何建议。提前谢谢。
旁注:转发声明对象的继承意图的能力,但尚未定义,现在非常受欢迎,你不同意吗? E.g。
struct X {};
struct Y : public X;
struct Y : public X {};
修改
虽然这个问题确实是重复的,但我相信能够详细说明一个适用于我的用例的解决方案。但这不是这个问题的答案。正如peterchen在评论部分所指出的那样,继承层次结构已知可以进一步派生类,这对于解决我的情况中的问题至关重要。
首先,让我进一步解释用例。 Base
通常是struct
,通过继承Traits
基础来承载NonConstructible概念。
struct Traits {
private:
Traits ();
};
由于我们希望所有特征类都是不可构造的,但仍然是定义的类型,因此出现了对均匀性的渴望。为此,我们可能同意继承Traits
。不同的用例可能要求覆盖(隐藏)特征类Base
的不同元素。例如,
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;
通常需要组合这些独立覆盖的特征类。因此,我们引入了一个实用程序MergeTraits
struct。
template <typename... taTraits>
struct MergeTraits : public taTraits... {};
我们希望这能使我们做到以下一般:
typedef MergeTraits <SpecialPortTraits, SpecialSocketIdTraits> SpecialSocketIdAndPortTraits;
typedef Socket <SpecialSocketIdAndPortTraits> Socket3;
但是,由于两个traits类都有::SocketIdType
,因此查找不明确。考虑当前的情况表明,默认情况下在层次结构中具有虚拟继承可以解决模糊问题,但也可以满足您的期望。由于Base
只会从一次继承而SpecialSocketIdTraits
会隐藏::SocketIdType
Base
,因此我们保证SpecialSocketIdAndPortTraits::SocketIdType
等于unsigned int
}}。如果有另一个这样的
struct SpecialSocketIdTraits2 : public virtual Base {
typedef long SocketIdType;
};
然后,::SocketIdType
到MergeTraits <SpecialSocketIdTraits, SpecialSocketIdTraits2>
的查找将再次正确模糊。现在为解决方案,灵感来自peterchen的评论。我们可以检查包taTraits
是否存在从特定基本特征继承的结构。然后可以验证这些继承是虚拟的。
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
>
{};
我的代码中的真正解决方案有点复杂,并利用一些已经存在的模板元编程工具作为语法糖,但这就是它归结为。我们现在可以在MergeTraits
中使用此组件来检测来自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.");
};
缺点是我们需要用这些断言的列表来污染MergeTraits
,当然。但是,我们可以将检查委托给进一步派生的类,至少解决我的问题。
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.");
};