Pergunta

If I have a class Base, with at least one virtual function, and a class Derived which inherits singly from this then (uintptr_t)derived - (uintptr_t)static_cast<Base*>(derived) is guaranteed (by the Itanium ABI) to be zero, even though Derived is not standard layout. However in the general case this is not necessarily true (eg. multiple inheritance).

Is it possible to write a trait which can be used to detect if one class is the primary base class of another?

Useful sections from the Itanium ABI:

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

Primary base class

For a dynamic class, the unique base class (if any) with which it shares the virtual pointer at offset 0. It is the first (in direct base class order) non-virtual dynamic base class, if one exists.

Dynamic class

A class requiring a virtual table pointer (because it or its bases have one or more virtual member functions or virtual base classes).

Foi útil?

Solução

This will be part of the next standard This was part of the aborted TR2 via the std::bases and std::direct_bases traits. If you happen to be working with a compiler that includes the draft-TR2, you might have support for this. For example in 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;
}

This prints:

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

(The demangler is my own; you may have seen it elsewhere.)

I trust you can build a suitable "is polymorphic and has precisely zero or one bases" trait yourself.

Outras dicas

Below is a wild, not thoroughly tested attempt at doing something helpful with C++11 only (actually, it does not really require any C++11 feature, but it's easier to write this way).

However, this trait only checks the transitive closure of the "is primary base class of" property: I could not figure out a non-intrusive way of verifying whether a class is a direct base class of another class.

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

Here is an example:

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!");
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top