我有两个指向基类的指针,一个指向实际的基础对象,另一个指向派生对象。我还有一个为基类和派生类重载的非成员函数。我想使用多态来向下转换指针,以便调用正确的重载。
代码的输出是
stretch
但是所需的输出是
base downcast called
base
derived downcast called
base
有人可以解释输出,以及必须采取哪些措施才能获得所需的行为?
base
derived
编辑:将cout添加到downcast函数以说明函数覆盖/多态。
答案 0 :(得分:4)
您基本上是尝试使运行时多态性影响编译时重载解析。出于明显的原因,这是不可能的。函数重载是一个编译时功能,这意味着在编译时基于 static 类型的函数参数执行重载解析。
在您的情况下,选择要调用的foo
是基于静态典型: static 类型ptr1
和{{1 }和 static 类型的ptr2
和ptr1->downcast()
返回值。后者是两种情况下类型ptr2->downcast()
的左值(引用)。选择base
时没有涉及多态性。编译器不知道(并且不关心)在运行时,这些foo
引用之一实际上将引用base &
对象。
但是,如果您可以从derived
拨打a.downcast()
,您会发现朗姆酒时间多态仍然可以在foo
内部运行。 (此时由于foo
为非常量而无法进行此类调用)
答案 1 :(得分:1)
你需要dynamic_cast。你正在努力做的事情不会起作用。
C ++允许对重写函数的协变返回值。由于derived
继承自base
,因此符合条件。但是如果你调用函数的基础版本,你仍然可以得到IT类型返回的类型,而不是派生类的版本。要获得该返回类型,您必须已经转换,以便编译器知道该函数返回的类型。
答案 2 :(得分:0)
所以我们可以用一种通用的方式做到这一点。
template<class...>struct types{};
template<std::size_t I>using index=std::integral_constant<std::size_t, I>;
template<class T, class types>
struct get_index_of_type;
template<class T, class...Ts>
struct get_index_of_type<T, types<T,Ts...>>:
index<0>
{};
template<class T, class U, class...Ts>
struct get_index_of_type<T, types<U,Ts...>>:
index<get_index_of_type<T, types<Ts...>>{}+1>
{};
template<class R, class Types>
struct dynamic_dispatch;
template<class R, class...Ts>
struct dynamic_dispatch<R, types<Ts...>>
{
using fptr = R(*)(void const* pf, void* t);
template<class F>
std::array<fptr, sizeof...(Ts)>
make_table() const {
return {{
+[](void const* pf, void* t)->R{
auto* pt = static_cast< std::remove_reference_t<Ts>* >(t);
auto* f = static_cast< std::remove_reference_t<F> const* >(pf);
return (*f)(static_cast<Ts&&>(*pt));
}...
}};
}
void const* pf = nullptr;
std::array<fptr, sizeof...(Ts)> table;
dynamic_dispatch( dynamic_dispatch&& )=default;
dynamic_dispatch( dynamic_dispatch const& )=default;
dynamic_dispatch& operator=( dynamic_dispatch&& )=default;
dynamic_dispatch& operator=( dynamic_dispatch const& )=default;
template<class F,
std::enable_if_t< !std::is_same<std::decay_t<F>, dynamic_dispatch>{}, int> =0
>
dynamic_dispatch( F&& f ):
pf(std::addressof(f)),
table( make_table<std::decay_t<F>>() )
{}
template<class T>
R operator()( T&& t ) const {
return table[get_index_of_type<T,types<Ts...>>{}]( pf, std::addressof(t) );
}
};
dynamic_dispatch<R, types<a,b,c>>
接受任何可以使用a
,b
或c
中的任何一个调用的callable(确切类型,包括l / r值和const所需,所以make你的列表详细。没有完成隐式转换;这可以通过更多的工作来解决。)
现在,在名为base
的{{1}}中添加方法:
apply
在派生中覆盖它:
virtual void apply( dynamic_dispatch<void, types<base*, derived*>> f ) {
return f(this);
}
同一个身体。
现在主要:
virtual void apply( dynamic_dispatch<void, types<base*, derived*>> f ) override {
return f(this);
}
为了进一步阅读,我将删除的动态调度键入到函数对象的类型列表中。我为这个调度创建了一个视图。
auto super_foo = [](auto* x) {
return foo(*x);
};
int main()
{
base* ptr1 = new(base);
base* ptr2 = new(derived);
ptr1->apply(super_foo);
ptr2->apply(super_foo);
}
是一个单独的对象,表示super_foo
的整个重载集,允许它作为一个参数传递。
这也可以通过访客模式更常规地完成:
foo
然后您实施struct visitor {
void invoke( base* ) const = 0;
void invoke( derived* ) const = 0;
};
:
foo_visitor
我们写了一个struct foo_visitor:visitor {
void invoke( base* a ) const {return foo(*a);}
void invoke( derived* a ) const {return foo(*a);}
};
,其中包含apply
并visitor&
。
请注意,这种技术,特别是如果您巧妙地在.invoke(this)
之间进行选择而不是要求完全匹配,则允许通过递归实现多个调度多态(或者,您可以将Ts
更改为{{1多个类型包)。