Почему нельзя приводить Derived T::* в Base T::*?

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

Наиболее очевидное решение заключается в распределении кучи, поскольку производные типы бывают разных размеров. Однако мы должны иметь возможность использовать объединение для хранения даже самого большого типа в стеке без какого-либо дополнительного выделения. Это требует сохранения дополнительного указателя на базу вместе с объединением, и в то же время усложняет копирование и назначение.

Это решает последнюю проблему, сохраняя селектор члена как смещение от начала объединения, которое указывает на активного члена объединения. В 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.

1 ответ

Решение

Почему не разрешено разыгрывать Derived T::* в Base T::* ?

Потому что это не разрешено языковым стандартом. Единственный указатель на преобразование члена, который разрешен, кроме преобразования нулевого указателя на член, это преобразование типа cv T Base::* в cv T Derived::*, как указано в Стандартной части 4.11/2,

Я не знаю точную причину этого, но соответствующая проблема основного языка 794 была отклонена из-за "отсутствия консенсуса относительно внесения этого изменения на данном этапе процесса стандартизации". Недавние действия по этой проблеме указывают, что она может измениться в следующем стандарте C++ (C++17).

Другие вопросы по тегам