Учти это
class Base { };
class Derived : public Base { };
Base *f1(Derived *) { return {}; }
Derived *f2(Derived *) { return {}; } // covariant
Base *f3(Base *) { return {}; } // contravariant
Derived *f4(Base *) { return {}; } // covariant & contravariant
using Callback = Base *(*)(Derived *);
Callback pfunc1 = f1; // works of course
// These won't work...
Callback pfunc2 = f2;
Callback pfunc3 = f3;
Callback pfunc4 = f4;// So I have to make a wrapper for each
Base *f2_wrap(Derived *d)
{
return f2(d);
}
Base *f3_wrap(Derived *d)
{
return f3(d);
}
Base *f4_wrap(Derived *d)
{
return f4(d);
}
// Now it works
Callback pfunc2 = f2_wrap;
Callback pfunc3 = f3_wrap;
Callback pfunc4 = f4_wrap;
Так почему я не могу установить указатель на функцию, которая имеет Derived
объект в качестве возвращаемого значения или функция, которая имеет Base
объект в качестве аргумента (который работает в делегате c #)?
Я знаю, что могу обойтись с помощью функции-обертки, но почему она не является частью языковой функции?
Необработанные указатели функций совместимы только по присваиванию, когда типы точно совпадают
Тем не менее, вы можете использовать std::function
:
#include <functional>
struct Base{};
struct Derived: Base{};
auto f1( Derived* ) -> Base* { return 0; }
auto f2( Derived* ) -> Derived* { return 0; } // covariant
auto f3( Base* ) -> Base* { return 0; } // contravariant
auto f4( Base* ) -> Derived* { return 0; } // covariant & contravariant
auto main()
-> int
{
using Callback = std::function<auto( Derived* ) -> Base*>;
Callback pfunc1 = f1; // works
Callback pfunc2 = f2; // works
Callback pfunc3 = f3; // works
Callback pfunc4 = f4; // works
}
Правила переопределения виртуальных функций менее строгие: поддерживаются ковариантные результаты необработанного указателя и ссылочного типа, но это все. Нет противоречий.
#include <functional>
struct Base{};
struct Derived: Base{};
struct F{ virtual auto f( Derived* ) -> Base* = 0; };
#define R override { return 0; }
struct F1: F { auto f( Derived* ) -> Base* R };
struct F2: F { auto f( Derived* ) -> Derived* R }; // covariant, OK
struct F3: F { auto f( Base* ) -> Base* R }; // !contravariant
struct F4: F { auto f( Base* ) -> Derived* R }; // !covariant & contravariant
Результат компиляции с MinGW g ++ 7.3.0:
> g ++ -c 2.cpp 2.cpp: 11: 21: ошибка: «Base * F3 :: f (Base *)» помечена как «override», но не переопределяет struct F3: F {auto f (Base *) -> Base * R}; //! контравариантный ^ 2.cpp: 12: 21: ошибка: «Derived * F4 :: f (Base *)» помечена как «override», но не переопределяет struct F4: F {auto f (Base *) -> Производная * R}; //! ковариантный контравариантный
Ограничение необработанных указателей и ссылочных типов результатов для ковариации не является проблемой на практике. Например, очевидно ковариантная функция с результатом интеллектуального указателя может быть легко выражена как не виртуальная перегрузка, вызывающая виртуальную функцию с результатом необработанного указателя. Отсутствие поддержки противоречивости также не является проблемой на практике, но по той простой причине, что она никогда не нужна.
C ++ был разработан для работы таким образом, чтобы не принимать другой параметр., Даже если это производный класс.
Взгляни на эту тему Указатели на приведение функций, которые отличаются типом аргумента
C # имеет виртуальную машину и не допускает множественного наследования, поэтому C # имеет гораздо больше контроля в такой ситуации, вы не можете сравнивать C # и C ++.
Если вы хотите использовать приведение, вы можете сделать это таким образом, но вы допускаете любую ошибку в отношении производного и базового класса, если вы сделаете что-то не так, это приведет к сбою вашей программы.
class Base { };
class Derived : public Base { };
Base *f1(Derived *) { return {}; }
Derived *f2(Derived *) { return {}; } // covariant
Base *f3(Base *) { return {}; } // contravariant
Derived *f4(Base *) { return {}; } // covariant & contravariant
using Callback = Base *(*)(Derived *);
Callback pfunc1 = f1; // works of course
// These won't work (now it will work)...
Callback pfunc2 = (Callback)(f2); //explict cast
Callback pfunc3 = (Callback)f3;
Callback pfunc4 = (Callback)f4;