一种方法是通过
SFINAE
,最好的例子是:
#include <iostream>
#include <type_traits>
struct Shape {};
struct Circle : public Shape {};
template<class Bp, class Dp>
std::enable_if_t<std::is_base_of<Bp,Dp>::value,void>
call_fn(Dp const& obj, void (*pfn)(const Bp&))
{
pfn(obj);
}
void shape_func(const Shape& s) { std::cout << "shape_func called!\n"; }
void circle_func(const Circle& s) { std::cout << "circle_func called!\n"; }
int main()
{
Shape shape;
Circle circle;
call_fn(shape, shape_func);
call_fn(circle, circle_func);
call_fn(circle, shape_func);
}
输出
shape_func called!
circle_func called!
shape_func called!
工作原理
这个实现使用了一个简单的(可能太多)练习
std::enable_if
与
std::is_base_of
提供具有潜在两种不同类型(一种是对象,另一种是提供函数的参数列表)的限定重载解析。具体来说,这:
template<class Bp, class Dp>
std::enable_if_t<std::is_base_of<Bp,Dp>::value,void>
call_fn(Dp const& obj, void (*pfn)(const Bp&))
Bp
以某种方式
Dp
,然后提供类型(在本例中
void
). 然后使用该类型作为函数的结果类型。因此,对于第一次调用,经过推导后的结果实例化如下所示:
void call_fn(Shape const& obj, void (*pfn)(const Shape&))
这是我们想要的。第二次调用产生了类似的实例化:
void call_fn(Circle const& obj, void (*pfn)(const Circle&))
第三个实例化将产生以下结果:
void call_fn(Circle const& obj, void (*pfn)(const Shape&))
因为
是不同的,但是
是一种衍生物。
失败案例
Shape
从基类继承列表
Circle
#include <iostream>
#include <type_traits>
struct Shape {};
struct Circle {};
template<class Bp, class Dp>
std::enable_if_t<std::is_base_of<Bp,Dp>::value,void>
call_fn(Dp const& obj, void (*pfn)(const Bp&))
{
pfn(obj);
}
void shape_func(const Shape& s) { std::cout << "shape_func called!\n"; }
void circle_func(const Circle& s) { std::cout << "circle_func called!\n"; }
int main()
{
Shape shape;
Circle circle;
call_fn(shape, shape_func); // still ok.
call_fn(circle, circle_func); // still ok.
call_fn(circle, shape_func); // not OK. no overload available,
// since a Circle is not a Shape.
}
结果是第三次调用没有匹配的函数调用。