Фон: Многие функциональные языки поддерживают алгебраические типы данных, которые в некоторой степени можно эмулировать с помощью виртуальных функций и наследования.
Наиболее очевидное решение заключается в распределении кучи, поскольку производные типы
различные размеры. Однако мы должны иметь возможность использовать объединение для хранения даже самого большого типа в стеке без какого-либо дополнительного выделения. Это требует сохранения дополнительного указателя на базу вместе с объединением, и в то же время усложняет копирование и назначение.
Это решает последнюю проблему, сохраняя селектор члена как смещение от начала объединения, которое указывает на активного члена объединения. В C ++ есть указатели на члены, которые кажутся почти подходящими для этой цели, за исключением того, что указатель на каждый член будет иметь другой тип.
Вопрос: Почему нельзя приводить Derived T :: * к Base T :: *?
Вот игрушечный пример, не связанный с вышеизложенным, который сталкивается с тем же ограничением:
struct fish {};
struct shark : public fish {};
struct trout : public fish {};
struct aquarium
{
shark shark;
trout trout;
};
fish aquarium::* pick_dinner(bool dangerous = true)
{
if (dangerous)
{
return &aquarium::shark;
}
return &aquarium::trout;
}
#include <iostream>
void cook(fish&)
{
std::cerr << "Do it yourself\n";
}
int main()
{
aquarium spherical, hexagonal;
fish aquarium::*ingredient = pick_dinner();
cook(spherical.*ingredient);
cook(hexagonal.*ingredient);
}
Сгенерированная ошибка компиляции:
main.cpp:15:16: error: cannot initialize return object of type 'fish aquarium::*' with an rvalue of type 'shark aquarium::*'
return &aquarium::shark;
^~~~~~~~~~~~~~~~
main.cpp:17:12: error: cannot initialize return object of type 'fish aquarium::*' with an rvalue of type 'trout aquarium::*'
return &aquarium::trout;
^~~~~~~~~~~~~~~~
2 errors generated.
Почему не разрешено разыгрывать Derived T::*
в Base T::*
?
Потому что это не разрешено языковым стандартом. Единственный указатель на преобразование члена, который разрешен, кроме преобразования нулевого указателя на член, это преобразование типа cv T Base::*
в cv T Derived::*
, как указано в Стандартной части 4.11/2
,
Я не знаю точную причину этого, но соответствующий Основная языковая проблема 794 было отвергнуто из-за «нет единого мнения, чтобы сделать это изменение на данном этапе процесса стандартизации». Недавняя активность Эта проблема указывает на то, что она может измениться в следующем стандарте C ++ (C ++ 17).