C++ 键入trait以标识主基类

C++ 键入trait以标识主基类,c++,typetraits,memory-layout,vptr,itanium-abi,C++,Typetraits,Memory Layout,Vptr,Itanium Abi,如果我有一个至少有一个虚函数的类基类,以及一个单独继承自该类的派生类,那么即使派生不是标准布局,也可以保证(安腾ABI)为零。然而,在一般情况下,这并不一定正确(例如多重继承) 是否可以编写一个特征来检测一个类是否是另一个类的主要基类 安腾ABI中的有用部分: 主基类 对于动态类,它在偏移量0处与之共享虚拟指针的唯一基类(如果有)。 它是第一个(按直接基类顺序)非虚拟动态基类(如果存在) 动态类 需要虚拟表指针的类(因为它或其基类有一个或多个虚拟成员函数或虚拟基类) 这将是下一个标准的一部分,

如果我有一个至少有一个虚函数的类基类,以及一个单独继承自该类的派生类,那么即使派生不是标准布局,也可以保证(安腾ABI)为零。然而,在一般情况下,这并不一定正确(例如多重继承)

是否可以编写一个特征来检测一个类是否是另一个类的主要基类

安腾ABI中的有用部分:

主基类

对于动态类,它在偏移量0处与之共享虚拟指针的唯一基类(如果有)。 它是第一个(按直接基类顺序)非虚拟动态基类(如果存在)

动态类

需要虚拟表指针的类(因为它或其基类有一个或多个虚拟成员函数或虚拟基类)

这将是下一个标准的一部分,这是通过
std::base
std::direct_base
特征中止的TR2的一部分。如果您正在使用包含draft-TR2的编译器,您可能对此有支持。例如,在GCC 4.7.2中:

#include <demangle.hpp>
#include <iostream>
#include <tr2/type_traits>

struct T1 { };
struct T2 { };
struct Foo : T1, T2 { };


int main()
{
    std::cout << demangle<std::tr2::direct_bases<Foo>::type>() << std::endl;
}
#包括
#包括
#包括
结构T1{};
结构T2{};
结构Foo:T1,T2{};
int main()
{

下面的std::cout是一个野生的、未经彻底测试的尝试,试图只使用C++11做一些有帮助的事情(实际上,它并不需要任何C++11特性,但这样编写更容易)

然而,这个特性只检查“is primary基类of”属性的传递闭包:我无法找到一种非侵入性的方法来验证一个类是否是另一个类的直接基类

#include <type_traits>

template<typename B, typename D, D* p = nullptr, typename = void>
struct is_primary_base_of : std::false_type { };

template<typename B, typename D, D* p>
struct is_primary_base_of<B, D, p,
    typename std::enable_if<
        ((int)(p + 1024) - (int)static_cast<B*>(p + 1024)) == 0
        >::type
    >
    :
    std::true_type { };
#包括
模板
结构是:std::false_类型{}的_primary_base_;
模板
结构是::类型的\u主\u基\u
>
:
std::true_type{};
以下是一个例子:

struct A { virtual ~A() { } };

struct B : A { };

struct C { virtual ~C() { } };

struct D : B, C { };

struct E : virtual A, C { };

int main()
{
    // Does not fire (A is PBC of B, which is PBC of D)
    static_assert(is_primary_base_of<A, D>::value, "Error!");

    // Does not fire (B is PBC of C)
    static_assert(is_primary_base_of<B, D>::value, "Error!");

    // Fires (C is not PBC of D)
    static_assert(is_primary_base_of<C, D>::value, "Error!");

    // Fires (A is inherited virtually by E, so it is not PBC of E)
    static_assert(is_primary_base_of<A, E>::value, "Error!");

    // Does not fire (C is the first non-virtual base class of E)
    static_assert(is_primary_base_of<C, E>::value, "Error!");
}
struct A{virtual~A(){};
结构B:A{};
结构C{virtual~C(){};
结构D:B,C{};
结构E:虚A,C{};
int main()
{
//不点火(A是B的PBC,这是D的PBC)
static_assert(是::value的_primary_base_,“Error!”);
//不点火(B是PBC还是C)
static_assert(是::value的_primary_base_,“Error!”);
//火灾(C不是D的PBC)
static_assert(是::value的_primary_base_,“Error!”);
//激发(A实际上由E继承,因此它不是E的PBC)
static_assert(是::value的_primary_base_,“Error!”);
//不激发(C是E的第一个非虚拟基类)
static_assert(是::value的_primary_base_,“Error!”);
}

您的测试表达式与引用的定义完全不匹配。您正在查看基子对象的地址,而ABI正在讨论v表中条目的顺序。@BenVoigt:发现得很好。我们真的应该有一个到ABI的链接。我非常好奇这些信息可以用来做什么。@BenVoigt我正在查看第2.4节,我的印象是虚拟表布局全部包含在第2.5节中?@ CuryyDeDy我试图创建一些有用的但非常非法的C++。它实际上是一个Booost::变体,但是对于一个无界的派生类集合(给定它不太大/对齐)。使用placement-move构造放置派生类型,然后提取基类型并调用其虚拟析构函数。添加指针调整值将删除此要求,但我不想添加此要求。这应该是一长串静态_断言的一部分。这不是测试ABI所说的内容(可以理解,因为问题也弄错了)@BenVoigt:好吧,如果你能澄清我应该测试什么,我可以尝试更改答案。否则,我会删除它。这是一个不应该删除的有用答案。但你可能想讨论实例布局与v-table布局。我不知道在不调用未定义行为的情况下检查v-table布局的任何方法(例如,将指向成员函数的指针转换为整数类型)。我确实认为您的答案将受益于测试用例这包括在基列表中不同位置的非多态基类。@BenVoigt:好的,我会把它放在那里,也许OP可以得到一些好处。关于测试用例,是的,这还没有被彻底测试过,但现在我不太确定我应该测试什么。太棒了!我一直想要这样的东西,虽然现在我记不起我在做什么了VE最初想要它。ItAiabi中的“基本基”与C++标准中的“直接基”不同。它可以有多个直接基类,但只有一个基类可以是主基。
struct A { virtual ~A() { } };

struct B : A { };

struct C { virtual ~C() { } };

struct D : B, C { };

struct E : virtual A, C { };

int main()
{
    // Does not fire (A is PBC of B, which is PBC of D)
    static_assert(is_primary_base_of<A, D>::value, "Error!");

    // Does not fire (B is PBC of C)
    static_assert(is_primary_base_of<B, D>::value, "Error!");

    // Fires (C is not PBC of D)
    static_assert(is_primary_base_of<C, D>::value, "Error!");

    // Fires (A is inherited virtually by E, so it is not PBC of E)
    static_assert(is_primary_base_of<A, E>::value, "Error!");

    // Does not fire (C is the first non-virtual base class of E)
    static_assert(is_primary_base_of<C, E>::value, "Error!");
}