考虑这个
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#委托中有效) )?
我知道我可以使用包装函数来解决问题,但是为什么它不是语言功能的一部分?
答案 0 :(得分:2)
仅当类型完全匹配时,原始函数指针才与分配兼容。
但是,您可以使用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: error: 'Base* F3::f(Base*)' marked 'override', but does not override struct F3: F { auto f( Base* ) -> Base* R }; // !contravariant ^ 2.cpp:12:21: error: 'Derived* F4::f(Base*)' marked 'override', but does not override struct F4: F { auto f( Base* ) -> Derived* R }; // !covariant & contravariant
对协方差的原始指针和引用结果类型的限制在实践中不是问题。例如,具有智能指针结果的表面上协变量函数可以很容易地表示为调用具有原始指针结果的虚拟函数的非虚拟重载。在实践中,缺乏对反方差的支持同样不是问题,而是出于一个根本不需要它的简单原因。
答案 1 :(得分:0)
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;