Насколько "криминальным" является такой доступ к нестатическим методам класса? Делал в Visual studio 2019 Community, с дефолтными настройками. Переносимость на другие платформы не важна, важно, чтобы это работало в дальнейших версиях Visual C++
#include <iostream>
class Base {
public:
int val;
Base():val(0) {}
virtual int Func1(int newval)
{
val = newval;
return val;
}
};
class Child : public Base{
public:
virtual int Func2(int newval)
{
val = newval*newval;
return val;
}
virtual int Func1(int newval)
{
val = newval * 2;
return val;
}
Child() : Base() {}
};
class Child2 : public Child
{
private:
virtual int Func2(int newval)
{
val = newval * newval * newval;
return val;
}
};
int main()
{
Base base;
Child child;
Child2 child2;
int (Base::*func)(int) = &Base::Func1;
int (Base::*func2)(int) = reinterpret_cast<int (Base::*)(int)>(&Child::Func2);
Base* t = &base;
(t->*func)(3);
std::cout << "base::val=" << base.val << "\n";
t = &child;
(t->*func)(3);
std::cout << "child::val=" << child.val << "\n";
(t->*func2)(3);
std::cout << "child::val=" << child.val << "\n";
t = &child2;
(t->*func)(3);
std::cout << "child2::val=" << child2.val << "\n";
(t->*func2)(3);
std::cout << "child2::val=" << child2.val << "\n";
getchar();
}
Выводит следующее:
base::val=3
child::val=6
child::val=9
child2::val=6
child2::val=27
Пробовал и релиз и дебаг и х86 и х64 - везде результат одинаков, ошибок не выдается.
В вашем коде все было бы абсолютно легально, если бы не неуместное использование reinterpret_cast.
Это контравариантное преобразование типов (преобразование указателя на член класса в направлении от потомка к предку) поддерживается в С++. Но делается оно через static_cast
int (Base::*func2)(int) = static_cast<int (Base::*)(int)>(&Child::Func2);
Обратите внимание, что даже несмотря на то, что в классе Base нет метода Func2, это преобразование является полностью легальным и определенным. Язык С++ требует наличия соответствующего метода Func2 в динамическом типе объекта только в момент вызова через такой указатель. В момент выполнения преобразования и инициализации указателя таких требований не накладывается.
То есть если бы далее вдруг сделали
Base* t = &base;
(t->*func2)(3); // UB
то вы бы наткнулись на неопределенное поведение при вызове. Но в вашем коде таких нарушений нет.
Из-за неуместного использования reinterpret_cast вместо static_cast ваш код имеет неопределенное поведение. В остальном никаких проблем в коде нет. Да, окончательное разрешение вызовов виртуальных методов класса через указатель в С++ делается в момент вызова. То есть все должно работать именно так, как оно работает в вашем примере.
Замените reinterpret_cast на static_cast и вы получите корректный код с полностью определенным спецификацией языка С++ поведением. Никакой "завязки на компилятор" в этом коде нет. Скорее наоборот, именно в VS для того, чтобы заставить аналогичный код корректно работать в более сложных случаях (множественное наследование, виртуальное наследование) вам придется повозиться с #pragma pointers_to_members. В других компиляторах все будет работать сразу.
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости