Как понизить рейтинг от неполиморфного виртуального базового класса?

Есть ли способ перехода из виртуального базового класса в производный класс, если в нем нет виртуальных функций? Вот код, демонстрирующий то, о чем я говорю:

struct Base1
{
int data;
};

struct Base2
{
char odd_size[9];
};

struct ViBase
{
double value;
};struct MostDerived : Base1, Base2, virtual ViBase
{
bool ok;
};void foo(ViBase &v)
{
MostDerived &md = somehow_cast<MostDerived&>(v);  //but HOW?
md.ok = true;
}int main()
{
MostDerived md;
foo(md);
}

Обратите внимание, что код только для демонстрации. Мой реальный сценарий довольно сложен и включает в себя параметры шаблона и приведение их к другому, зная только то, что первый является основой второго; это может быть обычная или виртуальная база, и она может иметь или не иметь виртуальные функции. (См. Упрощенный пример внизу). Я могу обнаружить полиморфный случай и виртуальный / не виртуальный базовый случай, используя черты типа, и решить все из них, кроме неполиморфной виртуальной базы. Так вот о чем я спрашиваю.

Я не могу придумать способ сделать актерский состав:

  • Неявные преобразования правильны; они только поднимают.

  • static_cast явно запрещено для приведения из виртуального базового класса:

    5.2.9 / 2 … а также B не является ни виртуальным базовым классом D ни базовый класс виртуального базового класса D, …

  • dynamic_cast тоже не могу этого сделать, так как для отката требуется полиморфный класс

    5.2.7 / 6 Иначе, v должен быть указателем или glvalue полиморфного типа (10.3).

    10.3 / 1 … Класс, который объявляет или наследует виртуальную функцию, называется полиморфный класс.

  • reinterpret_cast здесь не применяется

Если MostDerived имел по крайней мере одну виртуальную функцию, это, конечно, можно решить с помощью dynamic_cast, Но когда это не так, есть ли способ сделать бросок?

(ПРИМЕЧАНИЕ Все цитаты взяты из C ++ 11 черновик N3485)


В свете комментариев, слишком сосредоточенных на приведенном выше примере кода, вот набросок моей реальной ситуации:

template <class T_MostDerived>
struct Bar
{
template <class T_Base>
void foo(T_Base &b, typename std::enable_if<std::is_base_of<T_Base, T_MostDerived>::value>::type * = nullptr)
{
T_MostDerived &md = somehow_cast<T_MostDerived>(b);
do_stuff_with(md);
}
};

То есть я знаю что T_Base это базовый класс T_MostDerived (и я знаю что T_MostDerived действительно самый производный тип), но я ничего о них не знаю; Bar мой код, часть библиотеки, которую могут использовать неизвестные клиенты. Я могу обнаружить что это не полиморфная виртуальная база, но я не могу бросать это в таком случае.

5

Решение

Существует неявное недвусмысленное преобразование из MostDerived& к его ViBase&, static_cast может выразить такое преобразование явно, а также может сделать обратное преобразование. Это те виды конверсий, которые static_cast делает.

Как ФП отметил static_cast Вниз с виртуальной базы недействителен.

Исходный код ниже иллюстрирует почему:

#include <iostream>
using namespace std;

struct B { virtual ~B(){} };
struct D: virtual B {};
struct E: virtual B {};
struct X: D, E {};

auto main() -> int
{
X   x;
B&  b = static_cast<E&>( x );

// Can't do the following for the address adjustment that would work for
// D sub-object won't work for E sub-object, yet declarations of D and E
// are identical -- so the address adjustment can't be inferred from that.
//
//static_cast<D&>( b );

// This is OK:
dynamic_cast<D&>( b );
}

По сути, как это видно, вы не можете вывести корректировку адреса из объявления D (или же E) в одиночестве. И ни один не может компилятор. Это также исключает reinterpret_cast,

4

Другие решения

Это требует взлома. Понижение требует математики, поскольку множественное наследование может поставить базовый класс в произвольную позицию в производном классе. Однако, если вы знаете, что базовый класс фактически наследуется, то в производном классе должен быть только один его экземпляр. Это означает, что вы можете создать функцию преобразования:

struct MostDerived : Base1, Base2, virtual ViBase
{
bool ok;
template <typename T> static MostDerived * somehow_cast (T *v) {
static MostDerived derived;
static T &from = derived;
static size_t delta
= reinterpret_cast<char *>(&from) - reinterpret_cast<char *>(&derived);
char *to = reinterpret_cast<char *>(v);
return reinterpret_cast<MostDerived *>(to - delta);
}
};

То, что специальные приведения C ++ дают вам, что эта функция не делает, это безопасность типов. Эта функция вслепую предполагает, что ViBase имеет соответствующего производного потомка, которого обычно нет.

2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector