Черта типа для идентификации основного базового класса

Если у меня есть класс 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. Это первый (в прямом порядке базовых классов) невиртуальный динамический базовый класс, если он существует.

Динамический класс

Класс, требующий указателя виртуальной таблицы (поскольку он или его базы имеют одну или несколько виртуальных функций-членов или виртуальных базовых классов).


person jleahy    schedule 28.02.2013    source источник
comment
Ваше тестовое выражение совсем не соответствует цитируемому определению. Вы смотрите на адрес базового подобъекта, в то время как ABI обсуждает порядок записей внутри v-таблицы.   -  person Ben Voigt    schedule 01.03.2013
comment
@BenVoigt: Хорошо замечено. У нас действительно должна быть связь с ABI.   -  person Kerrek SB    schedule 01.03.2013
comment
Мне очень любопытно, для чего может быть использована эта информация.   -  person Edward Strange    schedule 01.03.2013
comment
@BenVoigt Я просматривал раздел 2.4, у меня сложилось впечатление, что весь макет виртуальной таблицы содержится в разделе 2.5?   -  person jleahy    schedule 01.03.2013
comment
@CrazyEddie Я пытаюсь создать что-то полезное, но очень незаконное C++. Фактически это boost::variant, но для неограниченного набора производных классов (учитывая, что он не слишком велик/выровнен). Производный тип размещается с помощью конструкции перемещения размещения, затем извлекается базовый тип и вызывается его виртуальный деструктор. Добавление значения корректировки указателя сняло бы это требование, но я не хотел этого добавлять. Это должно быть частью длинного списка static_asserts.   -  person jleahy    schedule 01.03.2013
comment
Что вы имеете в виду под первичным? Основной в данном базовом подобъекте? Первичный в объекте данного динамического типа?   -  person curiousguy    schedule 11.07.2018
comment
@jleahy Производный тип помещается, поэтому тип является полным типом объекта? Вы должны добавить эту информацию к вопросу.   -  person curiousguy    schedule 11.07.2018
comment
@BenVoigt ABI обсуждает порядок записей внутри v-таблицы повторное использование записей vtable таким образом подразумевает, что адрес объекта тот же   -  person curiousguy    schedule 28.11.2018


Ответы (2)


Это будет частью следующего стандарта Это было частью прерванного TR2 через черты std::bases и std::direct_bases. Если вы работаете с компилятором, который включает в себя 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>

(Деманглер принадлежит мне; возможно, вы видели его где-то еще.)

Я полагаю, что вы можете создать подходящую черту «является полиморфной и имеет ровно ноль или одну основу» самостоятельно.

person Kerrek SB    schedule 28.02.2013
comment
Потрясающий! Давно хотел что-то подобное, хотя сейчас даже не могу вспомнить, для чего вообще изначально хотел. - person GManNickG; 01.03.2013
comment
Первичная база в Itanium ABI отличается от прямой базы в стандарте C++. Можно иметь несколько прямых базовых классов, но только один из них может быть основным. - person Praxeolitic; 29.12.2014

Ниже приведена дикая, не проверенная до конца попытка сделать что-то полезное только с 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!");
}
person Andy Prowl    schedule 28.02.2013
comment
Это не проверка того, о чем говорит ABI (понятно, потому что вопрос тоже неверен). - person Ben Voigt; 01.03.2013
comment
@BenVoigt: Хорошо, может быть, если бы вы могли уточнить, что я должен проверить, я мог бы попытаться изменить ответ. Иначе просто удалю - person Andy Prowl; 01.03.2013
comment
Это полезный ответ, который не следует удалять. Но вы можете обсудить макет экземпляра и макет v-таблицы. Я не знаю, как проверить макет v-таблицы, не вызывая неопределенное поведение (например, приведение указателя к функциям-членам к целочисленным типам). - person Ben Voigt; 01.03.2013
comment
Я действительно думаю, что ваш ответ выиграет от тестовых случаев, которые включают неполиморфные базовые классы в разных местах в базовом списке. - person Ben Voigt; 01.03.2013
comment
@BenVoigt: Хорошо, тогда я оставлю это там, может быть, ОП может получить какую-то пользу. Что касается тестовых случаев, да, это не было тщательно проверено, но теперь я не уверен, что я должен тестировать. - person Andy Prowl; 01.03.2013
comment
Я понятия не имею, что означает ваш код. Можете ли вы объяснить (int)(p + 1024)? Что такое p? - person curiousguy; 11.07.2018
comment
Я не могу заставить этот код работать даже в простейшем примере ни на одном компиляторе, который я пробовал: godbolt.org/z/ QIS8i0 — я просмотрел ICC, GCC, Clang, во всех них is_primary_base всегда возвращает false. Что мне не хватает? - person John Zwinck; 25.09.2018
comment
Вы не можете использовать reinterpret_cast (или выполняющее его приведение в стиле C) в константном выражении, хотя компиляторы не всегда строго следят за его соблюдением. - person Davis Herring; 04.12.2019