Есть ли способ перехода из виртуального базового класса в производный класс, если в нем нет виртуальных функций? Вот код, демонстрирующий то, о чем я говорю:
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
мой код, часть библиотеки, которую могут использовать неизвестные клиенты. Я могу обнаружить что это не полиморфная виртуальная база, но я не могу бросать это в таком случае.
Существует неявное недвусмысленное преобразование из 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
,
Это требует взлома. Понижение требует математики, поскольку множественное наследование может поставить базовый класс в произвольную позицию в производном классе. Однако, если вы знаете, что базовый класс фактически наследуется, то в производном классе должен быть только один его экземпляр. Это означает, что вы можете создать функцию преобразования:
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
имеет соответствующего производного потомка, которого обычно нет.