输入trait以标识主要基类

时间:2013-02-28 19:55:59

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

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

是否有可能编写一个特征,可用于检测一个类是否是另一个类的主要基类?

Itanium ABI的有用部分:

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

主要基类

  

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

动态类

  

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

2 个答案:

答案 0 :(得分:8)

这将成为下一个标准的一部分这是通过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>

(demangler是我自己的;你可能已经在别处看过了。)

我相信你可以建立一个合适的“多态性,并且具有正好零或一个基础”的特征。

答案 1 :(得分:0)

下面是一个疯狂的,没有经过彻底测试的尝试,只做一些对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!");
}