通常比较c ++中的继承层次结构中的对象 [英] Generically comparing objects in an inheritance hierarchy in c++
问题描述
我打算这样写一个多重图片
std :: multimap< key,base_ptr> mymap;
我想能够存储许多派生类的指针> Der1
现在当我到达时试图插入一个对象到地图中,我首先做一个查找键,然后我需要比较如果对象是EQUIVALENT(不必是同一个对象,因此不进行指针比较)到那个位置。所以为此,让我说我覆盖 ==运算符
或者写一些比较函数。现在我想写一个这样的代码,当添加新的派生类时,我不必更改或添加任何东西。
必须有一个通用的方式来写这个。
code> class Base
{
virtual Base * get(){return this; }
virtual bool isEqual(const Base& toObj){
....
}
}
class Der1
{
Der1 * get(){return this; }
bool isEqual(const Der1& toObj){
....
}
}
但这似乎也不工作。因为当我这样做:
Base * bp1;
Base * bp2;
bp1-> get() - > isEqual(*(bp2-> get()))
b $ b
我看到对 get()
的调用结束于 get()
我想要的派生类,但然后编译器将返回的值处理为 Base *
。这很可能是因为它的运行时多态性。但我发现很难相信,这不会是一个优雅和明显的方式做到这一点。
有人建议。
=http://en.wikipedia.org/wiki/Multiple_dispatch =nofollow>多次派遣:
以下方式可能有帮助++ 11): http://ideone.com/lTsc7M
#include< cstdint>
#include< array>
#include< iostream>
#include< tuple>
#include< type_traits>
/////////////////////////
#if 1 //多发送
// size_t的序列//不在C ++ 11
模板< std :: size_t ...> struct index_sequence {};
//创建index_sequence< 0,>
template< std :: size_t N,std :: size_t ... Is>
struct make_index_sequence:make_index_sequence< N-1,N-1,Is ...> {};
template< std :: size_t ... Is>
struct make_index_sequence< 0,Is ...> :index_sequence< Is ...> {};
//通用IVisitor
//使用MyIVisitor = IVisitorTs< Child1,Child2,...>
template< typename ... Ts> class IVisitorTs;
template< typename T,typename ... Ts>
class IVisitorTs< T,Ts ...> :public IVisitorTs< Ts ...>
{
public:
using tuple_type = std :: tuple< T,Ts ...>
使用IVisitorTs< Ts ...> :: visit;
virtual void visit(const T& t)= 0;
};
template< typename T> class IVisitorTs< T>
{
public:
using tuple_type = std :: tuple< T> ;;
virtual void visit(const T& t)= 0;
};
namespace detail {
//检索T中的T的索引...
template< typename T,typename ... Ts> struct get_index;
template< typename T,typename ... Ts>
struct get_index< T,T,Ts ...> :std :: integral_constant< std :: size_t,0> {};
template< typename T,typename Tail,typename ... Ts>
struct get_index< T,Tail,Ts ...> :
std :: integral_constant< std :: size_t,1 + get_index< T,Ts ...> :: value> {};
//检索T中的T的索引< Ts ...>
template< typename T,typename Tuple> struct get_index_in_tuple;
template< typename T,template< typename ...>类C,类型名... Ts>
struct get_index_in_tuple< T,C< Ts ...> :get_index< T,Ts ...> {};
//获取多元组的元素
模板< std :: size_t I>
struct multi_array_getter
{
template< typename T,std :: size_t N>
static constexpr auto get(const T& a,const std :: array< std :: size_t,N& index)
- > decltype(multi_array_getter< I-1> :: get(a [index [N-I]],index))
{
return multi_array_getter& - I]],index);
}
};
模板<>
struct multi_array_getter< 0>
{
template< typename T,std :: size_t N>
static constexpr auto get(const T& a,const std :: array< std :: size_t,N& index)
- > decltype(a)
{
return a;
}
};
//通过转发到C实现(可以是非虚拟的)提供访问者
//的实现
template< typename IVisitor,typename C,typename ... Ts> struct IVisitorImpl;
template< typename IVisitor,typename C,typename T,typename ... Ts>
struct IVisitorImpl< IVisitor,C,T,Ts ...> :IVisitorImpl< IVisitor,C,Ts ...>
{
virtual void visit(const T& t)override {C :: visit(t); }
};
template< typename IVisitor,typename C,typename T>
struct IVisitorImpl< IVisitor,C,T> :IVisitor,C
{
virtual void visit(const T& t)override {C :: visit(t); }
};
//帮助将子类型展开为IVisitorImpl
template< typename IVisitor,typename C>
struct IVisitorImplType;
template< typename ... Ts,typename C>
struct IVisitorImplType< IVisitorTs< Ts ...> ;, C>
{
使用type = IVisitorImpl< IVisitorTs< Ts ...>,C,Ts ...>
};
//创建一个函数的多指针数组
//(所有组合的重载)。
template< typename Ret,typename F,typename Arg>
class GetAllOverload
{
private:
template< typename ... Ts>
struct Functor
{
//函数将在数组中。
static Ret call(F& f,const Arg& arg)
{
return call_helper(f,arg,make_index_sequence< sizeof ...(Ts)>
}
private:
//最后一个分派的函数
template< std :: size_t ... Is>
static Ret call_helper(F& f,const Arg& arg,index_sequence< Is ...>)
{
使用RetTuple = std :: tuple< Ts& ;
//如果arg是抽象类型,则静态转换是有效的
//当给定arg是具体类型时,reinterpret_cast是必需的。
// TODO:构建一个只有可能值的较小的表,以避免
return f(reinterpret_cast< typename std :: tuple_element< Is,RetTuple> :: type>(std :: get& (arg))...)
}
};
//帮助类创建函数指针的多数组
template< std :: size_t N,typename Tuple,typename ... Ts>
struct Builder;
template< typename ... Ts,typename ... Ts2>
struct Builder< 1,std :: tuple< Ts ...>,Ts2 ...>
{
使用RetType = std :: array< Ret(*)(F& const Arg&),sizeof ...(Ts)> ;;
static constexpr RetType build()
{
return RetType {& Functor< Ts2 ...,Ts> :: call ...}
}
};
template< std :: size_t N,typename ... Ts,typename ... Ts2>
struct Builder< N,std :: tuple< Ts ...>,Ts2 ...>
{
template< typename T>
使用RecType = Builder< N-1,std :: tuple< Ts ...>,Ts2 ...,T&
使用T0 = typename std :: tuple_element< 0,std :: tuple< Ts ...>> :: type;
使用RetType = std :: array< decltype(RecType< T0> :: build()),sizeof ...(Ts)> ;;
static constexpr RetType build(){
return RetType {RecType< Ts> :: build()...}
}
};
public:
template< std :: size_t N,typename VisitorTuple>
static constexpr auto get()
- > decltype(Builder< N,VisitorTuple> :: build())
{
return Builder< N,VisitorTuple&
}
};
template< typename Ret,typename IVisitor,typename F,std :: size_t N>
class dispatcher
{
private:
std :: array< std :: size_t,N>指数;
struct visitorCallImpl
{
template< typename T>
void visit(const T&)const
{
* index = get_index_in_tuple< T,IVisitor> :: value;
}
void setIndexPtr(std :: size_t& index){this-> index =& index; }
private:
std :: size_t * index = nullptr;
};
template< std :: size_t I,typename Tuple>
void set_index(const Tuple& t)
{
使用VisitorType = typename IVisitorImplType< IVisitor,visitorCallImpl&
VisitorType visitor;
visitor.setIndexPtr(index [I]);
std :: get< I>(t).accept(visitor);
}
public:
template< typename Tuple,std :: size_t ... Is>
Ret operator()(F& f,const Tuple& t,index_sequence< Is ...>)
{
const int dummy [] = {(set_index& (t),0)...}
static_cast< void>(dummy); // silent the warning unused varaible
constexpr auto a = GetAllOverload< Ret,F&& amp;&&&&&&& amp; 模板get< sizeof ...(Is),typename IVisitor :: tuple_type> );
auto func = multi_array_getter< N> :: get(a,index);
return(* func)(f,t);
}
};
} //命名空间细节
模板< typename Ret,typename访问者,类型名F,类型名... Ts>
Ret dispatch(F&& f,Ts& ... args)
{
constexpr std :: size_t size = sizeof ...(Ts);
detail :: dispatcher< Ret,Visitor,F&&&&&& d;
return d(std :: forward F(f),std :: tie(args ...),make_index_sequence< size>());
}
#endif //多个分派
#if 1 //多个分派用法
struct Square;
struct Rect;
struct Circle;
使用IShapeVisitor = IVisitorTs< Square,Rect,Circle> ;;
struct IShape {
virtual〜IShape()= default;
virtual void accept(IShapeVisitor&)const = 0;
};
struct Rect:IShape {
virtual void accept(IShapeVisitor& v)const override {v.visit(* this); }
};
struct Square:Rect {
virtual void accept(IShapeVisitor& v)const override {v.visit(* this); }
};
struct Circle:IShape {
virtual void accept(IShapeVisitor& v)const override {v.visit(* this); }
};
class ShapePrinter:public IShapeVisitor
{
public:
void visit(const Rect& s)override {std :: cout< Rect; }
void visit(const Square& s)override {std :: cout< 广场; }
void visit(const Circle& s)override {std :: cout< 圈; }
};
struct IsEqual
{
bool operator()(IShape& s1,IShape& s2)const
{
ShapePrinter printer;
s1.accept(printer);
std :: cout<< !=;
s2.accept(printer);
std :: cout<< std :: endl;
return false;
}
template< typename S>
bool operator()(S& s1,S& s2)const
{
ShapePrinter printer;
s1.accept(printer);
std :: cout<< ==;
s2.accept(printer);
std :: cout<< std :: endl;
return true;
}
};
int main(int argc,char * argv [])
{
Rect rect;
平方
Circle c;
IShape * shapes [] = {& rect,& sq,& c};
for(auto shape1:shapes){
for(auto shape2:shapes){
dispatch< bool,IShapeVisitor>(IsEqual(),* shape1,* shape2);
}
}
return 0;
}
#endif //多个分派用法
I am planning to write a multimap like this
std::multimap <key, base_ptr> mymap;
And I would like to be able to store pointers of many derived classes (say Der1
, Der2
) which derive from the base.
Now when I am trying to insert an object into the map, I first do a lookup on the key and then I need to compare if the object is EQUIVALENT (does not have to be the same object hence not doing a pointer comparison) to the one at that location. So for this lets say I override the == operator
or write some kind of a compare function. Now I would like to write the code for this in such a manner that when new derived classes are added, I dont have to change or add anything.
So I am thinking there has to be a generic way of writing this. But not able to think of one.
I was thinking of something like the following
class Base
{
virtual Base * get() { return this; }
virtual bool isEqual(const Base& toObj) {
....
}
}
class Der1
{
Der1 * get() { return this; }
bool isEqual(const Der1& toObj) {
....
}
}
But this does not seem to work either. because when I do:
Base* bp1;
Base* bp2;
bp1->get()->isEqual(*(bp2->get()))
I see that the call to get()
does end up in the get()
of the derived class as I expect, but then the compiler treats the returned value as Base*
. This is most likely because its a run-time polymorphism. But I find it hard to believe that there wont be an elegant and obvious way to do this.
Can somebody advise.
You may use Multiple dispatch:
Following may help (require C++11): http://ideone.com/lTsc7M
#include <cstdint>
#include <array>
#include <iostream>
#include <tuple>
#include <type_traits>
/////////////////////////
#if 1 // multiple dispatch
// sequence of size_t // not in C++11
template <std::size_t ...> struct index_sequence {};
// Create index_sequence<0, >
template <std::size_t N, std::size_t ...Is>
struct make_index_sequence : make_index_sequence <N - 1, N - 1, Is... > {};
template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
// Generic IVisitor
// Do: using MyIVisitor = IVisitorTs<Child1, Child2, ...>
template <typename ... Ts> class IVisitorTs;
template <typename T, typename ... Ts>
class IVisitorTs<T, Ts...> : public IVisitorTs<Ts...>
{
public:
using tuple_type = std::tuple<T, Ts...>;
using IVisitorTs<Ts...>::visit;
virtual void visit(const T& t) = 0;
};
template <typename T> class IVisitorTs<T>
{
public:
using tuple_type = std::tuple<T>;
virtual void visit(const T& t) = 0;
};
namespace detail {
// retrieve the index of T in Ts...
template <typename T, typename ... Ts> struct get_index;
template <typename T, typename ... Ts>
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename ... Ts>
struct get_index<T, Tail, Ts...> :
std::integral_constant < std::size_t, 1 + get_index<T, Ts...>::value > {};
// retrieve the index of T in Tuple<Ts...>
template <typename T, typename Tuple> struct get_index_in_tuple;
template <typename T, template <typename...> class C, typename ... Ts>
struct get_index_in_tuple<T, C<Ts...>> : get_index<T, Ts...> {};
// get element of a multiarray
template <std::size_t I>
struct multi_array_getter
{
template <typename T, std::size_t N>
static constexpr auto get(const T& a, const std::array<std::size_t, N>& index)
-> decltype(multi_array_getter<I - 1>::get(a[index[N - I]], index))
{
return multi_array_getter<I - 1>::get(a[index[N - I]], index);
}
};
template <>
struct multi_array_getter<0>
{
template <typename T, std::size_t N>
static constexpr auto get(const T& a, const std::array<std::size_t, N>& index)
-> decltype(a)
{
return a;
}
};
// Provide an implementation of visitor
// by forwarding to C implementation (which may be non virtual)
template <typename IVisitor, typename C, typename...Ts> struct IVisitorImpl;
template <typename IVisitor, typename C, typename T, typename...Ts>
struct IVisitorImpl<IVisitor, C, T, Ts...> : IVisitorImpl<IVisitor, C, Ts...>
{
virtual void visit(const T& t) override { C::visit(t); }
};
template <typename IVisitor, typename C, typename T>
struct IVisitorImpl<IVisitor, C, T> : IVisitor, C
{
virtual void visit(const T& t) override { C::visit(t); }
};
// helper to expand child type to IVisitorImpl
template <typename IVisitor, typename C>
struct IVisitorImplType;
template <typename ... Ts, typename C>
struct IVisitorImplType<IVisitorTs<Ts...>, C>
{
using type = IVisitorImpl<IVisitorTs<Ts...>, C, Ts...>;
};
// Create an multi array of pointer of function
// (with all combinaisons of overload).
template <typename Ret, typename F, typename Arg>
class GetAllOverload
{
private:
template <typename...Ts>
struct Functor
{
// function which will be in array.
static Ret call(F&f, const Arg& arg)
{
return call_helper(f, arg, make_index_sequence<sizeof...(Ts)>());
}
private:
// The final dispatched function
template <std::size_t ... Is>
static Ret call_helper(F&f, const Arg& arg, index_sequence<Is...>)
{
using RetTuple = std::tuple<Ts&...>;
// static cast is suffisant if arg is the abstract type
// when given arg is concrete type, reinterpret_cast is required.
// TODO: build a smaller table with only possible value to avoid that
return f(reinterpret_cast<typename std::tuple_element<Is, RetTuple>::type>(std::get<Is>(arg))...);
}
};
// helper class to create the multi array of function pointer
template <std::size_t N, typename Tuple, typename...Ts>
struct Builder;
template <typename...Ts, typename...Ts2>
struct Builder<1, std::tuple<Ts...>, Ts2...>
{
using RetType = std::array<Ret (*)(F&, const Arg&), sizeof...(Ts)>;
static constexpr RetType build()
{
return RetType{ &Functor<Ts2..., Ts>::call... };
}
};
template <std::size_t N, typename ...Ts, typename...Ts2>
struct Builder<N, std::tuple<Ts...>, Ts2...>
{
template <typename T>
using RecType = Builder<N - 1, std::tuple<Ts...>, Ts2..., T>;
using T0 = typename std::tuple_element<0, std::tuple<Ts...>>::type;
using RetType = std::array<decltype(RecType<T0>::build()), sizeof...(Ts)>;
static constexpr RetType build() {
return RetType{ RecType<Ts>::build()... };
}
};
public:
template <std::size_t N, typename VisitorTuple>
static constexpr auto get()
-> decltype(Builder<N, VisitorTuple>::build())
{
return Builder<N, VisitorTuple>::build();
}
};
template <typename Ret, typename IVisitor, typename F, std::size_t N>
class dispatcher
{
private:
std::array<std::size_t, N> index;
struct visitorCallImpl
{
template <typename T>
void visit(const T&) const
{
*index = get_index_in_tuple<T, IVisitor>::value;
}
void setIndexPtr(std::size_t& index) { this->index = &index; }
private:
std::size_t* index = nullptr;
};
template <std::size_t I, typename Tuple>
void set_index(const Tuple&t)
{
using VisitorType = typename IVisitorImplType<IVisitor, visitorCallImpl>::type;
VisitorType visitor;
visitor.setIndexPtr(index[I]);
std::get<I>(t).accept(visitor);
}
public:
template <typename Tuple, std::size_t ... Is>
Ret operator () (F&& f, const Tuple&t, index_sequence<Is...>)
{
const int dummy[] = {(set_index<Is>(t), 0)...};
static_cast<void>(dummy); // silent the warning unused varaible
constexpr auto a = GetAllOverload<Ret, F&&, Tuple>::
template get<sizeof...(Is), typename IVisitor::tuple_type>();
auto func = multi_array_getter<N>::get(a, index);
return (*func)(f, t);
}
};
} // namespace detail
template <typename Ret, typename Visitor, typename F, typename ... Ts>
Ret dispatch(F&& f, Ts&...args)
{
constexpr std::size_t size = sizeof...(Ts);
detail::dispatcher<Ret, Visitor, F&&, size> d;
return d(std::forward<F>(f), std::tie(args...), make_index_sequence<size>());
}
#endif // multiple dispatch
#if 1 // multiple dispatch usage
struct Square;
struct Rect;
struct Circle;
using IShapeVisitor = IVisitorTs<Square, Rect, Circle>;
struct IShape {
virtual ~IShape() = default;
virtual void accept(IShapeVisitor&) const = 0;
};
struct Rect : IShape {
virtual void accept(IShapeVisitor& v) const override { v.visit(*this); }
};
struct Square : Rect {
virtual void accept(IShapeVisitor& v) const override { v.visit(*this); }
};
struct Circle : IShape {
virtual void accept(IShapeVisitor& v) const override { v.visit(*this); }
};
class ShapePrinter : public IShapeVisitor
{
public:
void visit(const Rect& s) override { std::cout << "Rect"; }
void visit(const Square& s) override { std::cout << "Square"; }
void visit(const Circle& s) override { std::cout << "Circle"; }
};
struct IsEqual
{
bool operator() (IShape& s1, IShape& s2) const
{
ShapePrinter printer;
s1.accept(printer);
std::cout << " != ";
s2.accept(printer);
std::cout << std::endl;
return false;
}
template <typename S>
bool operator() (S& s1, S& s2) const
{
ShapePrinter printer;
s1.accept(printer);
std::cout << " == ";
s2.accept(printer);
std::cout << std::endl;
return true;
}
};
int main(int argc, char *argv[])
{
Rect rect;
Square sq;
Circle c;
IShape* shapes[] = { &rect, &sq, &c };
for (auto shape1 : shapes) {
for (auto shape2 : shapes) {
dispatch<bool, IShapeVisitor>(IsEqual(), *shape1, *shape2);
}
}
return 0;
}
#endif // multiple dispatch usage
这篇关于通常比较c ++中的继承层次结构中的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!