通常比较c ++中的继承层次结构中的对象 [英] Generically comparing objects in an inheritance hierarchy in c++

查看:154
本文介绍了通常比较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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆