【问题标题】:Type trait to identify primary base class键入特征以识别主要基类
【发布时间】:2013-02-15 04:07:41
【问题描述】:

如果我有一个类 Base,至少有一个虚函数,以及一个从该类单独继承的 Derived 类,那么即使 Derived 不是标准布局,(uintptr_t)derived - (uintptr_t)static_cast<Base*>(derived) (由 Itanium ABI)保证为零。但是在一般情况下,这不一定是正确的(例如多重继承)。

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

Itanium ABI 中的有用部分:

http://refspecs.linux-foundation.org/cxxabi-1.83.html

主基类

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

动态类

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

【问题讨论】:

  • 您的测试表达式与引用的定义完全不匹配。您正在查看基本子对象的地址,而 ABI 正在讨论 v-table 中的条目顺序。
  • @BenVoigt:很好看。我们真的应该有一个指向 ABI 的链接。
  • 我非常好奇这些信息可以用来做什么。
  • @BenVoigt 我在看第 2.4 节,我的印象是虚拟表布局都包含在第 2.5 节中?
  • @CrazyEddie 我正在尝试创建一些有用但非常非法的 C++ 的东西。它实际上是一个 boost::variant,但是对于一组无限的派生类(假设它不是太大/对齐)。派生类型使用放置移动构造进行放置,然后提取基类型并调用其虚拟析构函数。添加指针调整值将删除此要求,但我不想添加。这应该是一长串 static_asserts 的一部分。

标签: c++ typetraits memory-layout vptr itanium-abi


【解决方案1】:

这将成为下一个标准的一部分这是通过 std::basesstd::direct_bases 特征中止的 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;
}

打印出来:

std::tr2::__reflection_typelist<T1, T2>

(拆线器是我自己的;你可能在别处见过。)

我相信您可以自己构建一个合适的“是多态的并且恰好具有零个或一个碱基”的特征。

【讨论】:

  • 太棒了!多年来我一直想要这样的东西,但现在我什至不记得我最初想要它是为了什么。
  • 安腾 ABI 中的“主基”不同于 C++ 标准中的“直接基”。可以有多个直接基类,但只有一个基类可以是主要的。
【解决方案2】:

下面是一个狂野的、未经彻底测试的尝试,仅对 C++11 有帮助(实际上,它真的不需要任何 C++11 功能,但编写它更容易大大地)。

然而,这个特性只检查“是主要基类”属性的传递闭包:我想不出一种非侵入式的方式来验证一个类是否是一个直接另一个类的基类。

#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 { };

这是一个例子:

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

【讨论】:

  • 这不是在测试 ABI 在说什么(可以理解,因为问题也弄错了)。
  • @BenVoigt:好的,如果你能澄清我应该测试什么,我可以尝试改变答案。不然我就删了
  • 这是一个有用的答案,不应删除。但是您可能想讨论实例布局与 v-table 布局。我不知道有什么方法可以在不调用未定义行为的情况下检查 v-table 布局(例如,将指向成员函数的指针转换为整数类型)。
  • 我确实认为您的答案会受益于在基列表中不同位置包含非多态基类的测试用例。
  • @BenVoigt:好的,我会把它留在那里,也许 OP 可以获得一些好处。关于测试用例,是的,这还没有经过彻底测试,但现在我不确定应该测试什么
猜你喜欢
  • 2021-09-28
  • 1970-01-01
  • 2016-12-31
  • 2020-03-02
  • 2011-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-09
相关资源
最近更新 更多