Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 具有完全可维护性的多分派解决方案_C++_Virtual_Multiple Dispatch - Fatal编程技术网

C++ 具有完全可维护性的多分派解决方案

C++ 具有完全可维护性的多分派解决方案,c++,virtual,multiple-dispatch,C++,Virtual,Multiple Dispatch,有人能想出一个好方法来实现多重分派,比如下面的Object::foo重载吗 class A { public: virtual void accept (Visitor&) = 0; }; class B : public A { virtual void accept (Visitor&) override; }; class C : public A { virtual void accept (Visitor&) over

有人能想出一个好方法来实现多重分派,比如下面的
Object::foo
重载吗

class A {
    public:
        virtual void accept (Visitor&) = 0;
};

class B : public A {
    virtual void accept (Visitor&) override;
};

class C : public A {
    virtual void accept (Visitor&) override;
};

class D : public A {
    virtual void accept (Visitor&) override;
};

class Object {
    public:
        virtual double foo (A*, A*) { std::cout << "Object::foo A,A\n";  return 3.14; }
        virtual double foo (B*, B*) { std::cout << "Object::foo B,B\n";  return 3.14; }
        virtual double foo (B*, C*) { std::cout << "Object::foo B,C\n";  return 3.14; }
        virtual double foo (C*, B*) { std::cout << "Object::foo C,B\n";  return 3.14; }
        virtual double foo (C*, C*) { std::cout << "Object::foo C,C\n";  return 3.14; }
        virtual char foo (A*, A*, A*) const { std::cout << "Object::foo A,A,A\n";  return '&'; }
        virtual char foo (C*, B*, D*) const { std::cout << "Object::foo C,B,D\n";  return '!'; }  // Overload of foo with three arguments.
        virtual void bar (A*, A*, A*) const { std::cout << "Object::bar A,A,A\n"; }
        virtual void bar (B*, B*, B*) const { std::cout << "Object::bar B,B,B\n"; }
        virtual void bar (B*, C*, B*) const { std::cout << "Object::bar B,C,B\n"; }
        virtual void bar (B*, C*, C*) const { std::cout << "Object::bar B,C,C\n"; }
        virtual void bar (B*, C*, D*) const { std::cout << "Object::bar B,C,D\n"; }
        virtual void bar (C*, B*, D*) const { std::cout << "Object::bar C,B,D\n"; }
        virtual void bar (C*, C*, C*) const { std::cout << "Object::bar C,C,C\n"; }
        virtual void bar (D*, B*, C*) const { std::cout << "Object::bar D,B,C\n"; }
        double fooMultipleDispatch (A*, A*);
        char fooMultipleDispatch (A*, A*, A*);
        void barMultipleDispatch (A*, A*, A*);
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        double multipleDispatch (const ObjectFooVisitor<2>& visitor, const Z1<Is...>&, const Z2<Js...>&) {return foo (visitor.getArray<Is>()[Js]...);}
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        char multipleDispatch (const ObjectFooVisitor<3>& visitor, const Z1<Is...>&, const Z2<Js...>&) const {return foo (visitor.getArray<Is>()[Js]...);}
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        void multipleDispatch (const ObjectBarVisitor& visitor, const Z1<Is...>&, const Z2<Js...>&) {bar (visitor.getArray<Is>()[Js]...);} 
};
所以我遵循的主要思想是存储动态类型指针数组的元组,然后使用此存储调用适当的重载。但必须有其他的设计才能让它更好地工作

如果你想看,这是我的完整解决方案(在GCC4.9.2上编译,需要SFINAE支持)。尽管可以尝试对其进行改进,使其更易于维护,但我确信需要一种新的设计

#include <iostream>
#include <array>
#include <tuple>
#include <type_traits>

class Object;  class B;  class C;  class D;

class Visitor {
    public:
        virtual void visit (B*) = 0;
        virtual void visit (C*) = 0;
        virtual void visit (D*) = 0;
};

class A {
    public:
        virtual void accept (Visitor&) = 0;
};

class B : public A {
    virtual void accept (Visitor&) override;
};

class C : public A {
    virtual void accept (Visitor&) override;
};

class D : public A {
    virtual void accept (Visitor&) override;
};

template <int, int> struct ArrayType;  // Extra template parameter N needed here to allow std::array of any size.
template <int N> struct ArrayType<N,0> { using type = std::array<B*, N>; };
template <int N> struct ArrayType<N,1> { using type = std::array<C*, N>; };
template <int N> struct ArrayType<N,2> { using type = std::array<D*, N>; };

template <int> class ObjectFooVisitor;

template<>
class ObjectFooVisitor<2> : public Visitor {  // For Object::foo overrides with two arguments.
    private:
        std::tuple<std::array<B*, 2>, std::array<C*, 2>> tupleOfArrays;
        std::array<int, 2> tupleIndices;
        int numAccepted = 0;
    protected:
        virtual void visit (B* b) override {std::get<0>(tupleOfArrays)[numAccepted] = b;  tupleIndices[numAccepted++] = 0;}
        virtual void visit (C* c) override {std::get<1>(tupleOfArrays)[numAccepted] = c;  tupleIndices[numAccepted++] = 1;}
        virtual void visit (D*) override {}
    public:
        ObjectFooVisitor() { std::get<0>(tupleOfArrays) = {{nullptr, nullptr}};  std::get<1>(tupleOfArrays) = {{nullptr, nullptr}}; }
        template <int N> const typename ArrayType<2,N>::type& getArray() const {return std::get<N>(tupleOfArrays);}
        const std::array<int, 2>& getTupleIndices() const {return tupleIndices;}
};

template<>
class ObjectFooVisitor<3> : public Visitor {  // For Object::foo overrides with three arguments.
    private:
        std::tuple<std::array<B*, 3>, std::array<C*, 3>, std::array<D*, 3>> tupleOfArrays;
        std::array<int, 3> tupleIndices;
        int numAccepted = 0;
    protected:
        virtual void visit (B* b) override {std::get<0>(tupleOfArrays)[numAccepted] = b;  tupleIndices[numAccepted++] = 0;}
        virtual void visit (C* c) override {std::get<1>(tupleOfArrays)[numAccepted] = c;  tupleIndices[numAccepted++] = 1;}
        virtual void visit (D* d) override {std::get<2>(tupleOfArrays)[numAccepted] = d;  tupleIndices[numAccepted++] = 2;}
    public:
        ObjectFooVisitor() { std::get<0>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<1>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<2>(tupleOfArrays) = {{nullptr, nullptr, nullptr}}; }
        template <int N> const typename ArrayType<3,N>::type& getArray() const {return std::get<N>(tupleOfArrays);}
        const std::array<int, 3>& getTupleIndices() const {return tupleIndices;}
};

class ObjectBarVisitor : public Visitor {
    private:
        std::tuple<std::array<B*, 3>, std::array<C*, 3>, std::array<D*, 3>> tupleOfArrays;
        std::array<int, 3> tupleIndices;
        int numAccepted = 0;
    protected:
        virtual void visit (B* b) override {std::get<0>(tupleOfArrays)[numAccepted] = b;  tupleIndices[numAccepted++] = 0;}
        virtual void visit (C* c) override {std::get<1>(tupleOfArrays)[numAccepted] = c;  tupleIndices[numAccepted++] = 1;}
        virtual void visit (D* d) override {std::get<2>(tupleOfArrays)[numAccepted] = d;  tupleIndices[numAccepted++] = 2;}
    public:
        ObjectBarVisitor() { std::get<0>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<1>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<2>(tupleOfArrays) = {{nullptr, nullptr, nullptr}}; }
        template <int N> const typename ArrayType<3,N>::type& getArray() const {return std::get<N>(tupleOfArrays);}
        const std::array<int, 3>& getTupleIndices() const {return tupleIndices;}
};

class ThingBazVisitor : public Visitor {
    private:
        std::tuple<std::array<B*, 3>, std::array<C*, 3>, std::array<D*, 3>> tupleOfArrays;
        std::array<int, 3> tupleIndices;
        int numAccepted = 0;
    protected:
        virtual void visit (B* b) override {std::get<0>(tupleOfArrays)[numAccepted] = b;  tupleIndices[numAccepted++] = 0;}
        virtual void visit (C* c) override {std::get<1>(tupleOfArrays)[numAccepted] = c;  tupleIndices[numAccepted++] = 1;}
        virtual void visit (D* d) override {std::get<2>(tupleOfArrays)[numAccepted] = d;  tupleIndices[numAccepted++] = 2;}
    public:
        ThingBazVisitor() { std::get<0>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<1>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<2>(tupleOfArrays) = {{nullptr, nullptr, nullptr}}; }
        template <int N> const typename ArrayType<3,N>::type& getArray() const {return std::get<N>(tupleOfArrays);}
        const std::array<int, 3>& getTupleIndices() const {return tupleIndices;}
};

void B::accept (Visitor& visitor) {visitor.visit(this);}
void C::accept (Visitor& visitor) {visitor.visit(this);}
void D::accept (Visitor& visitor) {visitor.visit(this);}

class Object {
    public:
        virtual double foo (A*, A*) { std::cout << "Object::foo A,A\n";  return 3.14; }
        virtual double foo (B*, B*) { std::cout << "Object::foo B,B\n";  return 3.14; }
        virtual double foo (B*, C*) { std::cout << "Object::foo B,C\n";  return 3.14; }
        virtual double foo (C*, B*) { std::cout << "Object::foo C,B\n";  return 3.14; }
        virtual double foo (C*, C*) { std::cout << "Object::foo C,C\n";  return 3.14; }
        virtual char foo (A*, A*, A*) const { std::cout << "Object::foo A,A,A\n";  return '&'; }  // This is needed for the foo overload to be multiple dispatched, even if it is never used, otherwise the other possible foo overloads with three arguments will have no place to go to.
        virtual char foo (C*, B*, D*) const { std::cout << "Object::foo C,B,D\n";  return '!'; }  // Overload of foo with three arguments.  Furthermore, the function itself is const and returns char instead of double.  Simply define char fooMultipleDispatch (A*, A*, A*), ObjectFooVisitor<3> (the old ObjectFooVisitor now renamed to ObjectFooVisitor<2>) and overload multipleDispatch with char multipleDispatch (const ObjectFooVisitor<3>& visitor, const Z1<Is...>&, const Z2<Js...>&). 
        virtual void bar (A*, A*, A*) const { std::cout << "Object::bar A,A,A\n"; }
        virtual void bar (B*, B*, B*) const { std::cout << "Object::bar B,B,B\n"; }
        virtual void bar (B*, C*, B*) const { std::cout << "Object::bar B,C,B\n"; }
        virtual void bar (B*, C*, C*) const { std::cout << "Object::bar B,C,C\n"; }
        virtual void bar (B*, C*, D*) const { std::cout << "Object::bar B,C,D\n"; }
        virtual void bar (C*, B*, D*) const { std::cout << "Object::bar C,B,D\n"; }
        virtual void bar (C*, C*, C*) const { std::cout << "Object::bar C,C,C\n"; }
        virtual void bar (D*, B*, C*) const { std::cout << "Object::bar D,B,C\n"; }
        double fooMultipleDispatch (A*, A*);
        char fooMultipleDispatch (A*, A*, A*);
        void barMultipleDispatch (A*, A*, A*);
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        double multipleDispatch (const ObjectFooVisitor<2>& visitor, const Z1<Is...>&, const Z2<Js...>&) {return foo (visitor.getArray<Is>()[Js]...);}
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        char multipleDispatch (const ObjectFooVisitor<3>& visitor, const Z1<Is...>&, const Z2<Js...>&) const {return foo (visitor.getArray<Is>()[Js]...);}
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        void multipleDispatch (const ObjectBarVisitor& visitor, const Z1<Is...>&, const Z2<Js...>&) {bar (visitor.getArray<Is>()[Js]...);} 
};

class Thing {
    public:
        virtual int baz (A*, A*, A*) { std::cout << "Thing::baz A,A,A\n";  return 5; }
        virtual int baz (B*, B*, B*) { std::cout << "Thing::baz B,B,B\n";  return 5; }
        virtual int baz (B*, C*, B*) { std::cout << "Thing::baz B,C,B\n";  return 5; }
        virtual int baz (B*, C*, C*) { std::cout << "Thing::baz B,C,C\n";  return 5; }
        virtual int baz (B*, C*, D*) { std::cout << "Thing::baz B,C,D\n";  return 5; }
        virtual int baz (C*, B*, D*) { std::cout << "Thing::baz C,B,D\n";  return 5; }
        virtual int baz (C*, C*, C*) { std::cout << "Thing::baz C,C,C\n";  return 5; }
        virtual int baz (D*, B*, C*) { std::cout << "Thing::baz D,B,C\n";  return 5; }
        int bazMultipleDispatch (A*, A*, A*);
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        int multipleDispatch (const ThingBazVisitor& visitor, const Z1<Is...>&, const Z2<Js...>&) {return baz (visitor.getArray<Is>()[Js]...);}  // Since Thing only has baz interested in multiple dispatching, it does not need its own MultipleDispatch inner class like Object does (but if other Thing methods want multiple dispatching, then as in the Object::multipleDispatch overloads).
};

template <typename, typename, typename, int, int, typename, typename = void, int = 0> struct MultipleDispatcher;

template <typename T, typename V, typename R, int Num, int N, template <int...> class Z, int... Is, int I>
struct MultipleDispatcher<T, V, R, Num, N, Z<I, Is...>, typename std::enable_if<N != Num-1>::type> : MultipleDispatcher<T, V, R, Num, N, Z<I+1, Is...>, typename std::enable_if<N != Num-1>::type> {
    T* t;
    const V& visitor;
    MultipleDispatcher (T* o, const V& v) : MultipleDispatcher<T, V, R, Num, N, Z<I+1, Is...>, typename std::enable_if<N != Num-1>::type>(o,v), t(o), visitor(v) {}
    R execute();
};

template <typename T, typename V, typename R, int Num, int Last, template <int...> class Z, int... Is, int I>
struct MultipleDispatcher<T, V, R, Num, Last, Z<I, Is...>, typename std::enable_if<Last == Num-1>::type> : MultipleDispatcher<T, V, R, Num, Last, Z<I+1, Is...>, typename std::enable_if<Last == Num-1>::type> {
    T* t;
    const V& visitor;
    MultipleDispatcher (T* o, const V& v) : MultipleDispatcher<T, V, R, Num, Last, Z<I+1, Is...>, typename std::enable_if<Last == Num-1>::type>(o,v), t(o), visitor(v) {}
    R execute();
};

template <typename T, typename V, typename R, int Num, int N, template <int...> class Z, int... Is>
struct MultipleDispatcher<T, V, R, Num, N, Z<Num, Is...>, typename std::enable_if<N != Num-1>::type> {
    T* t;
    const V& visitor;
    MultipleDispatcher (T* o, const V& v) : t(o), visitor(v) {}
    R execute() {return R();}  // End of recursion
};

template <typename T, typename V, typename R, int Num, int Last, template <int...> class Z, int... Is>
struct MultipleDispatcher<T, V, R, Num, Last, Z<Num, Is...>, typename std::enable_if<Last == Num-1>::type> {  // This unique specialization is needed to avoid compiling ambiguity.
    T* t;
    const V& visitor;
    MultipleDispatcher (T* o, const V& v) : t(o), visitor(v) {}
    R execute() {return R();}  // End of recursion
};

template <typename T, typename V, typename R, int Num, int N, template <int...> class Z, int... Is, int I>
R MultipleDispatcher<T, V, R, Num, N, Z<I, Is...>, typename std::enable_if<N != Num-1>::type>::execute() {
    if (I == visitor.getTupleIndices()[N])
        return MultipleDispatcher<T, V, R, Num, N+1, Z<0, I, Is...>, void>(t, visitor).execute();  // Do we need to specify the std::enable_if part here?  Apparently not.  We will allow N+1 to be anything, and there is apparently no ambiguity.
    else
        return MultipleDispatcher<T, V, R, Num, N, Z<I+1, Is...>, typename std::enable_if<N != Num-1>::type>::execute();
}

template <int...> struct index_sequence {};

template <int N, int... Is>
struct make_index_sequence_helper : make_index_sequence_helper<N-1, N-1, Is...> {};

template <int... Is>
struct make_index_sequence_helper<0, Is...> {
    using type = index_sequence<Is...>;
};

template <int N>
using make_index_sequence = typename make_index_sequence_helper<N>::type;

template <typename, typename> struct ReverseHelper;

template <template <int...> class Z, typename Pack>
struct ReverseHelper<Z<>, Pack> {
    using type = Pack;
};

template <template <int...> class Z, int First, int... Rest, int... Is>
struct ReverseHelper<Z<First, Rest...>, Z<Is...>> : ReverseHelper<Z<Rest...>, Z<First, Is...>> {};

template <typename> struct Reverse;

template <template <int...> class Z, int... Is>
struct Reverse<Z<Is...>> : ReverseHelper<Z<Is...>, Z<>> {};

template <typename T, typename V, typename R, int Num, int Last, template <int...> class Z, int... Is, int I>
R MultipleDispatcher<T, V, R, Num, Last, Z<I, Is...>, typename std::enable_if<Last == Num-1>::type>::execute() {
    if (I == visitor.getTupleIndices()[Last])
        return t->template multipleDispatch (visitor, typename Reverse<Z<I, Is...>>::type{}, make_index_sequence<Num>{});  // This compiles on GCC 4.9.2 but not on GCC 4.8.1. Template disambiguator needed.
    else
        return MultipleDispatcher<T, V, R, Num, Last, Z<I+1, Is...>, typename std::enable_if<Last == Num-1>::type>::execute();
}

double Object::fooMultipleDispatch (A* a1, A* a2) {
    ObjectFooVisitor<2> visitor;
    a1->accept(visitor);  // Stores the dynamic type of a1
    a2->accept(visitor);  // and a2 into ObjectFooVisitor<2>'s array data members.
    return MultipleDispatcher<Object, ObjectFooVisitor<2>, double, 2, 0, index_sequence<0>>(this, visitor).execute();  // 2 because there are two arguments in the Object::foo overloads.
}

char Object::fooMultipleDispatch (A* a1, A* a2, A* a3) {
    ObjectFooVisitor<3> visitor;
    a1->accept(visitor);
    a2->accept(visitor);
    a3->accept(visitor);
    return MultipleDispatcher<Object, ObjectFooVisitor<3>, char, 3, 0, index_sequence<0>>(this, visitor).execute();  // 3 because there are three arguments in this particular Object::foo overload.
}

void Object::barMultipleDispatch (A* a1, A* a2, A* a3) {
    ObjectBarVisitor visitor;
    a1->accept(visitor);
    a2->accept(visitor);
    a3->accept(visitor);
    MultipleDispatcher<Object, ObjectBarVisitor, void, 3, 0, index_sequence<0>>(this, visitor).execute();  // 3 because there are two arguments in the Object::foo overloads.
}

int Thing::bazMultipleDispatch (A* a1, A* a2, A* a3) {
    ThingBazVisitor visitor;
    a1->accept(visitor);
    a2->accept(visitor);
    a3->accept(visitor);
    return MultipleDispatcher<Thing, ThingBazVisitor, int, 3, 0, index_sequence<0>>(this, visitor).execute();  // 3 because there are three arguments in the Thing::baz overloads.
}

// Test

int main() {
    A* a[] = {new B, new C, new D};
    Object* object = new Object;

    double d = object->foo (a[0], a[1]);  // Object::foo A,A  (no multiple dispatch)
    d = object->fooMultipleDispatch (a[0], a[1]);  // Object::foo B,C
    std::cout << "d = " << d << std::endl;  // 3.12

    const char k = object->fooMultipleDispatch (a[1], a[0], a[2]);  // Object::foo C,B,D
    std::cout << "k = " << k << std::endl;  // !

    object->bar (a[1], a[0], a[2]);  // Object::bar A,A,A  (no multiple dispatch)
    object->barMultipleDispatch (a[1], a[0], a[2]);  // Object::bar C,B,D

    Thing* thing = new Thing;
    int num = thing->baz (a[1], a[0], a[2]);  // Thing::baz A,A,A  (no multiple dispatch)  
    num = thing->bazMultipleDispatch (a[1], a[0], a[2]);  // Thing::baz C,B,D
    std::cout << "num = " << num << std::endl;  // 5
}
#包括
#包括
#包括
#包括
类对象;乙级;;丙级;;D类;
班级访客{
公众:
虚拟无效访问(B*)=0;
虚拟无效访问(C*)=0;
虚拟无效访问(D*)=0;
};
甲级{
公众:
虚拟无效接受(访问者&)=0;
};
B类:公共A{
虚拟无效接受(访问者&)覆盖;
};
C类:公共A类{
虚拟无效接受(访问者&)覆盖;
};
D类:公共A{
虚拟无效接受(访问者&)覆盖;
};
模板结构ArrayType;//这里需要额外的模板参数N来允许任何大小的std::数组。
模板结构ArrayType{using type=std::array;};
模板结构ArrayType{using type=std::array;};
模板结构ArrayType{using type=std::array;};
模板类objectfootvisor;
模板
类ObjectFooVisitor:public Visitor{//For Object::foo使用两个参数重写。
私人:
std::tuple-tupleof数组;
std::数组元组索引;
int numacepted=0;
受保护的:
虚拟无效访问(B*B)重写{std::get(tupleOfArrays)[numAccepted]=B;tupleIndices[numAccepted++]=0;}
虚拟无效访问(C*C)重写{std::get(tupleOfArrays)[numAccepted]=C;tupleIndices[numAccepted++]=1;}
虚拟无效访问(D*)重写{}
公众:
ObjectFooVisitor(){std::get(tupleOfArrays)={{nullptr,nullptr}};std::get(tupleOfArrays)={{nullptr,nullptr};}
模板常量typename ArrayType::type&getArray()常量{return std::get(tupleOfArrays);}
const std::array&gettupleIndexes()const{return tupleIndicates;}
};
模板
类ObjectFooVisitor:public Visitor{//For Object::foo用三个参数重写。
私人:
std::tuple-tupleof数组;
std::数组元组索引;
int numacepted=0;
受保护的:
虚拟无效访问(B*B)重写{std::get(tupleOfArrays)[numAccepted]=B;tupleIndices[numAccepted++]=0;}
虚拟无效访问(C*C)重写{std::get(tupleOfArrays)[numAccepted]=C;tupleIndices[numAccepted++]=1;}
虚拟无效访问(D*D)重写{std::get(tupleofarray)[numAccepted]=D;tupleIndices[numAccepted++]=2;}
公众:
ObjectFooVisitor(){std::get(TupleOfArray)={{nullptr,nullptr,nullptr}};std::get(TupleOfArray)={{nullptr,nullptr,nullptr};std::get(TupleOfArray)={{nullptr,nullptr,nullptr};}
模板常量typename ArrayType::type&getArray()常量{return std::get(tupleOfArrays);}
const std::array&gettupleIndexes()const{return tupleIndicates;}
};
类ObjectBarVisitor:公共访问者{
私人:
std::tuple-tupleof数组;
std::数组元组索引;
int numacepted=0;
受保护的:
虚拟无效访问(B*B)重写{std::get(tupleOfArrays)[numAccepted]=B;tupleIndices[numAccepted++]=0;}
虚拟无效访问(C*C)重写{std::get(tupleOfArrays)[numAccepted]=C;tupleIndices[numAccepted++]=1;}
虚拟无效访问(D*D)重写{std::get(tupleofarray)[numAccepted]=D;tupleIndices[numAccepted++]=2;}
公众:
ObjectBarVisitor(){std::get(TupleOfArray)={{nullptr,nullptr,nullptr}};std::get(TupleOfArray)={{nullptr,nullptr,nullptr};std::get(TupleOfArray)={{nullptr,nullptr,nullptr};}
模板常量typename ArrayType::type&getArray()常量{return std::get(tupleOfArrays);}
const std::array&gettupleIndexes()const{return tupleIndicates;}
};
类访客:公众访客{
私人:
std::tuple-tupleof数组;
std::数组元组索引;
int numacepted=0;
受保护的:
虚拟无效访问(B*B)重写{std::get(tupleOfArrays)[numAccepted]=B;tupleIndices[numAccepted++]=0;}
虚拟无效访问(C*C)重写{std::get(tupleOfArrays)[numAccepted]=C;tupleIndices[numAccepted++]=1;}
虚拟无效访问(D*D)重写{std::get(tupleofarray)[numAccepted]=D;tupleIndices[numAccepted++]=2;}
公众:
ThingBazVisitor(){std::get(tupleOfArrays)={{nullptr,nullptr,nullptr}};std::get(tupleOfArrays)={{nullptr,nullptr,nullptr};std::get(tupleOfArrays)={{nullptr,nullptr,nullptr};}
模板常量typename ArrayType::type&getArray()常量{return std::get(tupleOfArrays);}
const std::array&gettupleIndexes()const{return tupleIndicates;}
};
接受(访问者和访问者){Visitor.visit(this);}
接受(访问者和访问者){Visitor.visit(this);}
接受(访问者和访问者){Visitor.visit(this);}
类对象{
公众:

虚拟双foo(A*,A*){std::cout我会使用类似的东西,经过一些整理:

必要标题
#包括
#包括
#包括
#包括
#包括
#包括
#包括
dynamic_call:调用任何带有降级参数的函数,由调度器使用
//基本情况:无参数
模板
结果动态_调用(std::function fun)
{
返回乐趣();
}    
模板
结果动态调用(结果(*fun)()
{
返回乐趣();
}
//一个或多个参数:动态_转换第一个参数,
//递归地传递其余部分
模板
结果动态_调用(std::function fun、Arg0*Arg0、Args*…Args)
{
FunArg0*转换的_arg0=动态_转换(arg0);
template<>
class ObjectFooVisitor<2> : public Visitor {  // For Object::foo overrides with two arguments.
    private:
        std::tuple<std::array<B*, 2>, std::array<C*, 2>> tupleOfArrays;
        std::array<int, 2> tupleIndices;
// ....
};

double Object::fooMultipleDispatch (A* a1, A* a2) {
    ObjectFooVisitor<2> visitor;
    a1->accept(visitor);  // Stores the dynamic type of a1
    a2->accept(visitor);  // and a2 into ObjectFooVisitor<2>'s array data members.
    return MultipleDispatcher<Object, ObjectFooVisitor<2>, double, 2, 0, index_sequence<0>>(this, visitor).execute();  // 2 because there are two arguments in the Object::foo overloads.
}
#include <iostream>
#include <array>
#include <tuple>
#include <type_traits>

class Object;  class B;  class C;  class D;

class Visitor {
    public:
        virtual void visit (B*) = 0;
        virtual void visit (C*) = 0;
        virtual void visit (D*) = 0;
};

class A {
    public:
        virtual void accept (Visitor&) = 0;
};

class B : public A {
    virtual void accept (Visitor&) override;
};

class C : public A {
    virtual void accept (Visitor&) override;
};

class D : public A {
    virtual void accept (Visitor&) override;
};

template <int, int> struct ArrayType;  // Extra template parameter N needed here to allow std::array of any size.
template <int N> struct ArrayType<N,0> { using type = std::array<B*, N>; };
template <int N> struct ArrayType<N,1> { using type = std::array<C*, N>; };
template <int N> struct ArrayType<N,2> { using type = std::array<D*, N>; };

template <int> class ObjectFooVisitor;

template<>
class ObjectFooVisitor<2> : public Visitor {  // For Object::foo overrides with two arguments.
    private:
        std::tuple<std::array<B*, 2>, std::array<C*, 2>> tupleOfArrays;
        std::array<int, 2> tupleIndices;
        int numAccepted = 0;
    protected:
        virtual void visit (B* b) override {std::get<0>(tupleOfArrays)[numAccepted] = b;  tupleIndices[numAccepted++] = 0;}
        virtual void visit (C* c) override {std::get<1>(tupleOfArrays)[numAccepted] = c;  tupleIndices[numAccepted++] = 1;}
        virtual void visit (D*) override {}
    public:
        ObjectFooVisitor() { std::get<0>(tupleOfArrays) = {{nullptr, nullptr}};  std::get<1>(tupleOfArrays) = {{nullptr, nullptr}}; }
        template <int N> const typename ArrayType<2,N>::type& getArray() const {return std::get<N>(tupleOfArrays);}
        const std::array<int, 2>& getTupleIndices() const {return tupleIndices;}
};

template<>
class ObjectFooVisitor<3> : public Visitor {  // For Object::foo overrides with three arguments.
    private:
        std::tuple<std::array<B*, 3>, std::array<C*, 3>, std::array<D*, 3>> tupleOfArrays;
        std::array<int, 3> tupleIndices;
        int numAccepted = 0;
    protected:
        virtual void visit (B* b) override {std::get<0>(tupleOfArrays)[numAccepted] = b;  tupleIndices[numAccepted++] = 0;}
        virtual void visit (C* c) override {std::get<1>(tupleOfArrays)[numAccepted] = c;  tupleIndices[numAccepted++] = 1;}
        virtual void visit (D* d) override {std::get<2>(tupleOfArrays)[numAccepted] = d;  tupleIndices[numAccepted++] = 2;}
    public:
        ObjectFooVisitor() { std::get<0>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<1>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<2>(tupleOfArrays) = {{nullptr, nullptr, nullptr}}; }
        template <int N> const typename ArrayType<3,N>::type& getArray() const {return std::get<N>(tupleOfArrays);}
        const std::array<int, 3>& getTupleIndices() const {return tupleIndices;}
};

class ObjectBarVisitor : public Visitor {
    private:
        std::tuple<std::array<B*, 3>, std::array<C*, 3>, std::array<D*, 3>> tupleOfArrays;
        std::array<int, 3> tupleIndices;
        int numAccepted = 0;
    protected:
        virtual void visit (B* b) override {std::get<0>(tupleOfArrays)[numAccepted] = b;  tupleIndices[numAccepted++] = 0;}
        virtual void visit (C* c) override {std::get<1>(tupleOfArrays)[numAccepted] = c;  tupleIndices[numAccepted++] = 1;}
        virtual void visit (D* d) override {std::get<2>(tupleOfArrays)[numAccepted] = d;  tupleIndices[numAccepted++] = 2;}
    public:
        ObjectBarVisitor() { std::get<0>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<1>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<2>(tupleOfArrays) = {{nullptr, nullptr, nullptr}}; }
        template <int N> const typename ArrayType<3,N>::type& getArray() const {return std::get<N>(tupleOfArrays);}
        const std::array<int, 3>& getTupleIndices() const {return tupleIndices;}
};

class ThingBazVisitor : public Visitor {
    private:
        std::tuple<std::array<B*, 3>, std::array<C*, 3>, std::array<D*, 3>> tupleOfArrays;
        std::array<int, 3> tupleIndices;
        int numAccepted = 0;
    protected:
        virtual void visit (B* b) override {std::get<0>(tupleOfArrays)[numAccepted] = b;  tupleIndices[numAccepted++] = 0;}
        virtual void visit (C* c) override {std::get<1>(tupleOfArrays)[numAccepted] = c;  tupleIndices[numAccepted++] = 1;}
        virtual void visit (D* d) override {std::get<2>(tupleOfArrays)[numAccepted] = d;  tupleIndices[numAccepted++] = 2;}
    public:
        ThingBazVisitor() { std::get<0>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<1>(tupleOfArrays) = {{nullptr, nullptr, nullptr}};  std::get<2>(tupleOfArrays) = {{nullptr, nullptr, nullptr}}; }
        template <int N> const typename ArrayType<3,N>::type& getArray() const {return std::get<N>(tupleOfArrays);}
        const std::array<int, 3>& getTupleIndices() const {return tupleIndices;}
};

void B::accept (Visitor& visitor) {visitor.visit(this);}
void C::accept (Visitor& visitor) {visitor.visit(this);}
void D::accept (Visitor& visitor) {visitor.visit(this);}

class Object {
    public:
        virtual double foo (A*, A*) { std::cout << "Object::foo A,A\n";  return 3.14; }
        virtual double foo (B*, B*) { std::cout << "Object::foo B,B\n";  return 3.14; }
        virtual double foo (B*, C*) { std::cout << "Object::foo B,C\n";  return 3.14; }
        virtual double foo (C*, B*) { std::cout << "Object::foo C,B\n";  return 3.14; }
        virtual double foo (C*, C*) { std::cout << "Object::foo C,C\n";  return 3.14; }
        virtual char foo (A*, A*, A*) const { std::cout << "Object::foo A,A,A\n";  return '&'; }  // This is needed for the foo overload to be multiple dispatched, even if it is never used, otherwise the other possible foo overloads with three arguments will have no place to go to.
        virtual char foo (C*, B*, D*) const { std::cout << "Object::foo C,B,D\n";  return '!'; }  // Overload of foo with three arguments.  Furthermore, the function itself is const and returns char instead of double.  Simply define char fooMultipleDispatch (A*, A*, A*), ObjectFooVisitor<3> (the old ObjectFooVisitor now renamed to ObjectFooVisitor<2>) and overload multipleDispatch with char multipleDispatch (const ObjectFooVisitor<3>& visitor, const Z1<Is...>&, const Z2<Js...>&). 
        virtual void bar (A*, A*, A*) const { std::cout << "Object::bar A,A,A\n"; }
        virtual void bar (B*, B*, B*) const { std::cout << "Object::bar B,B,B\n"; }
        virtual void bar (B*, C*, B*) const { std::cout << "Object::bar B,C,B\n"; }
        virtual void bar (B*, C*, C*) const { std::cout << "Object::bar B,C,C\n"; }
        virtual void bar (B*, C*, D*) const { std::cout << "Object::bar B,C,D\n"; }
        virtual void bar (C*, B*, D*) const { std::cout << "Object::bar C,B,D\n"; }
        virtual void bar (C*, C*, C*) const { std::cout << "Object::bar C,C,C\n"; }
        virtual void bar (D*, B*, C*) const { std::cout << "Object::bar D,B,C\n"; }
        double fooMultipleDispatch (A*, A*);
        char fooMultipleDispatch (A*, A*, A*);
        void barMultipleDispatch (A*, A*, A*);
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        double multipleDispatch (const ObjectFooVisitor<2>& visitor, const Z1<Is...>&, const Z2<Js...>&) {return foo (visitor.getArray<Is>()[Js]...);}
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        char multipleDispatch (const ObjectFooVisitor<3>& visitor, const Z1<Is...>&, const Z2<Js...>&) const {return foo (visitor.getArray<Is>()[Js]...);}
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        void multipleDispatch (const ObjectBarVisitor& visitor, const Z1<Is...>&, const Z2<Js...>&) {bar (visitor.getArray<Is>()[Js]...);} 
};

class Thing {
    public:
        virtual int baz (A*, A*, A*) { std::cout << "Thing::baz A,A,A\n";  return 5; }
        virtual int baz (B*, B*, B*) { std::cout << "Thing::baz B,B,B\n";  return 5; }
        virtual int baz (B*, C*, B*) { std::cout << "Thing::baz B,C,B\n";  return 5; }
        virtual int baz (B*, C*, C*) { std::cout << "Thing::baz B,C,C\n";  return 5; }
        virtual int baz (B*, C*, D*) { std::cout << "Thing::baz B,C,D\n";  return 5; }
        virtual int baz (C*, B*, D*) { std::cout << "Thing::baz C,B,D\n";  return 5; }
        virtual int baz (C*, C*, C*) { std::cout << "Thing::baz C,C,C\n";  return 5; }
        virtual int baz (D*, B*, C*) { std::cout << "Thing::baz D,B,C\n";  return 5; }
        int bazMultipleDispatch (A*, A*, A*);
        template <template <int...> class Z1, template <int...> class Z2, int... Is, int... Js>
        int multipleDispatch (const ThingBazVisitor& visitor, const Z1<Is...>&, const Z2<Js...>&) {return baz (visitor.getArray<Is>()[Js]...);}  // Since Thing only has baz interested in multiple dispatching, it does not need its own MultipleDispatch inner class like Object does (but if other Thing methods want multiple dispatching, then as in the Object::multipleDispatch overloads).
};

template <typename, typename, typename, int, int, typename, typename = void, int = 0> struct MultipleDispatcher;

template <typename T, typename V, typename R, int Num, int N, template <int...> class Z, int... Is, int I>
struct MultipleDispatcher<T, V, R, Num, N, Z<I, Is...>, typename std::enable_if<N != Num-1>::type> : MultipleDispatcher<T, V, R, Num, N, Z<I+1, Is...>, typename std::enable_if<N != Num-1>::type> {
    T* t;
    const V& visitor;
    MultipleDispatcher (T* o, const V& v) : MultipleDispatcher<T, V, R, Num, N, Z<I+1, Is...>, typename std::enable_if<N != Num-1>::type>(o,v), t(o), visitor(v) {}
    R execute();
};

template <typename T, typename V, typename R, int Num, int Last, template <int...> class Z, int... Is, int I>
struct MultipleDispatcher<T, V, R, Num, Last, Z<I, Is...>, typename std::enable_if<Last == Num-1>::type> : MultipleDispatcher<T, V, R, Num, Last, Z<I+1, Is...>, typename std::enable_if<Last == Num-1>::type> {
    T* t;
    const V& visitor;
    MultipleDispatcher (T* o, const V& v) : MultipleDispatcher<T, V, R, Num, Last, Z<I+1, Is...>, typename std::enable_if<Last == Num-1>::type>(o,v), t(o), visitor(v) {}
    R execute();
};

template <typename T, typename V, typename R, int Num, int N, template <int...> class Z, int... Is>
struct MultipleDispatcher<T, V, R, Num, N, Z<Num, Is...>, typename std::enable_if<N != Num-1>::type> {
    T* t;
    const V& visitor;
    MultipleDispatcher (T* o, const V& v) : t(o), visitor(v) {}
    R execute() {return R();}  // End of recursion
};

template <typename T, typename V, typename R, int Num, int Last, template <int...> class Z, int... Is>
struct MultipleDispatcher<T, V, R, Num, Last, Z<Num, Is...>, typename std::enable_if<Last == Num-1>::type> {  // This unique specialization is needed to avoid compiling ambiguity.
    T* t;
    const V& visitor;
    MultipleDispatcher (T* o, const V& v) : t(o), visitor(v) {}
    R execute() {return R();}  // End of recursion
};

template <typename T, typename V, typename R, int Num, int N, template <int...> class Z, int... Is, int I>
R MultipleDispatcher<T, V, R, Num, N, Z<I, Is...>, typename std::enable_if<N != Num-1>::type>::execute() {
    if (I == visitor.getTupleIndices()[N])
        return MultipleDispatcher<T, V, R, Num, N+1, Z<0, I, Is...>, void>(t, visitor).execute();  // Do we need to specify the std::enable_if part here?  Apparently not.  We will allow N+1 to be anything, and there is apparently no ambiguity.
    else
        return MultipleDispatcher<T, V, R, Num, N, Z<I+1, Is...>, typename std::enable_if<N != Num-1>::type>::execute();
}

template <int...> struct index_sequence {};

template <int N, int... Is>
struct make_index_sequence_helper : make_index_sequence_helper<N-1, N-1, Is...> {};

template <int... Is>
struct make_index_sequence_helper<0, Is...> {
    using type = index_sequence<Is...>;
};

template <int N>
using make_index_sequence = typename make_index_sequence_helper<N>::type;

template <typename, typename> struct ReverseHelper;

template <template <int...> class Z, typename Pack>
struct ReverseHelper<Z<>, Pack> {
    using type = Pack;
};

template <template <int...> class Z, int First, int... Rest, int... Is>
struct ReverseHelper<Z<First, Rest...>, Z<Is...>> : ReverseHelper<Z<Rest...>, Z<First, Is...>> {};

template <typename> struct Reverse;

template <template <int...> class Z, int... Is>
struct Reverse<Z<Is...>> : ReverseHelper<Z<Is...>, Z<>> {};

template <typename T, typename V, typename R, int Num, int Last, template <int...> class Z, int... Is, int I>
R MultipleDispatcher<T, V, R, Num, Last, Z<I, Is...>, typename std::enable_if<Last == Num-1>::type>::execute() {
    if (I == visitor.getTupleIndices()[Last])
        return t->template multipleDispatch (visitor, typename Reverse<Z<I, Is...>>::type{}, make_index_sequence<Num>{});  // This compiles on GCC 4.9.2 but not on GCC 4.8.1. Template disambiguator needed.
    else
        return MultipleDispatcher<T, V, R, Num, Last, Z<I+1, Is...>, typename std::enable_if<Last == Num-1>::type>::execute();
}

double Object::fooMultipleDispatch (A* a1, A* a2) {
    ObjectFooVisitor<2> visitor;
    a1->accept(visitor);  // Stores the dynamic type of a1
    a2->accept(visitor);  // and a2 into ObjectFooVisitor<2>'s array data members.
    return MultipleDispatcher<Object, ObjectFooVisitor<2>, double, 2, 0, index_sequence<0>>(this, visitor).execute();  // 2 because there are two arguments in the Object::foo overloads.
}

char Object::fooMultipleDispatch (A* a1, A* a2, A* a3) {
    ObjectFooVisitor<3> visitor;
    a1->accept(visitor);
    a2->accept(visitor);
    a3->accept(visitor);
    return MultipleDispatcher<Object, ObjectFooVisitor<3>, char, 3, 0, index_sequence<0>>(this, visitor).execute();  // 3 because there are three arguments in this particular Object::foo overload.
}

void Object::barMultipleDispatch (A* a1, A* a2, A* a3) {
    ObjectBarVisitor visitor;
    a1->accept(visitor);
    a2->accept(visitor);
    a3->accept(visitor);
    MultipleDispatcher<Object, ObjectBarVisitor, void, 3, 0, index_sequence<0>>(this, visitor).execute();  // 3 because there are two arguments in the Object::foo overloads.
}

int Thing::bazMultipleDispatch (A* a1, A* a2, A* a3) {
    ThingBazVisitor visitor;
    a1->accept(visitor);
    a2->accept(visitor);
    a3->accept(visitor);
    return MultipleDispatcher<Thing, ThingBazVisitor, int, 3, 0, index_sequence<0>>(this, visitor).execute();  // 3 because there are three arguments in the Thing::baz overloads.
}

// Test

int main() {
    A* a[] = {new B, new C, new D};
    Object* object = new Object;

    double d = object->foo (a[0], a[1]);  // Object::foo A,A  (no multiple dispatch)
    d = object->fooMultipleDispatch (a[0], a[1]);  // Object::foo B,C
    std::cout << "d = " << d << std::endl;  // 3.12

    const char k = object->fooMultipleDispatch (a[1], a[0], a[2]);  // Object::foo C,B,D
    std::cout << "k = " << k << std::endl;  // !

    object->bar (a[1], a[0], a[2]);  // Object::bar A,A,A  (no multiple dispatch)
    object->barMultipleDispatch (a[1], a[0], a[2]);  // Object::bar C,B,D

    Thing* thing = new Thing;
    int num = thing->baz (a[1], a[0], a[2]);  // Thing::baz A,A,A  (no multiple dispatch)  
    num = thing->bazMultipleDispatch (a[1], a[0], a[2]);  // Thing::baz C,B,D
    std::cout << "num = " << num << std::endl;  // 5
}
#include <iostream>
#include <typeinfo>
#include <map>
#include <array>
#include <functional>
#include <stdexcept>
#include <algorithm>
// base case: no arguments    

template<typename Result>
Result dynamic_call (std::function<Result()> fun)
{
    return fun();
}    
template<typename Result>
Result dynamic_call (Result(*fun)())
{
    return fun();
}

// one or more argument: dynamic_cast the first argument,
// recursively pass down the rest of them

template<typename Result, typename Arg0, typename FunArg0, typename ... Args, typename ... FunArgs>
Result dynamic_call (std::function<Result(FunArg0*, FunArgs*...)> fun, Arg0* arg0, Args*... args)
{
    FunArg0* converted_arg0 = dynamic_cast<FunArg0*>(arg0);
    if (converted_arg0 == nullptr)
        throw std::runtime_error("Argument type error!");
    std::function<Result(FunArgs*...)> helper = [converted_arg0, fun](FunArgs*... fun_args) -> Result
    {
        return fun(converted_arg0, fun_args...);
    };
    return dynamic_call(helper, args...);
}

template<typename Result, typename Arg0, typename FunArg0, typename ... Args, typename ... FunArgs>
Result dynamic_call (Result (*fun)(FunArg0*, FunArgs*...), Arg0* arg0, Args*... args)
{
    std::function<Result(FunArg0*, FunArgs*...)> sfn(fun);
    return dynamic_call(sfn, arg0, args...);
}
template <typename Result, typename ... Args>
class Dispatcher
{
  public:

    Result operator() (Args*... args)
    {
        key k{tiholder(typeid(*args))...};
        typename map::iterator it = functions.find(k);
        if (it == functions.end())
            throw std::runtime_error("Function not found!");
        return it->second(args...);
    }

    template <typename ... FunArgs>
    void register_fn(std::function<Result(FunArgs*...)> fun)
    {
        auto lam = [fun](Args*... args) -> Result
        {
            return dynamic_call(fun, args...);
        };
        key k{tiholder(typeid(FunArgs))...};
        functions[k] = lam;
    }

    template <typename ... FunArgs>
    void register_fn(Result(*fun)(FunArgs*...))
    {
        return register_fn(std::function<Result(FunArgs*...)>(fun));
    }

  private:

    struct tiholder
    {
        const std::type_info* ti;
        tiholder(const std::type_info& ti) : ti(&ti) {}
        bool operator< (const tiholder& other) const { return ti->before(*other.ti); }
    };

    static constexpr int PackSize = sizeof ... (Args);
    using key = std::array<tiholder, PackSize>;
    using value = std::function<Result(Args*...)>;
    using map = std::map<key, value>;
    map functions;
};
struct Base { virtual ~Base() {} } ;

struct A : Base {};
struct B : Base {};

void foo1(A*,A*) { std::cout << "foo(A*,A*)\n"; }
void foo2(A*,B*) { std::cout << "foo(A*,B*)\n"; }
void foo3(B*,A*) { std::cout << "foo(B*,A*)\n"; }
void foo4(B*,B*) { std::cout << "foo(B*,B*)\n"; }
int main ()
{
    Base* x = new A;
    Base* y = new B;

    Dispatcher<void,Base,Base> foo;
    foo.register_fn(foo1);
    foo.register_fn(foo2);
    foo.register_fn(foo3);
    foo.register_fn(foo4);

    foo(x,x);
    foo(x,y);
    foo(y,x);
    foo(y,y);

}
// 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 ~IVisitorTs() = default;
    virtual void visit(const T& t) = 0;
};

template <typename T> class IVisitorTs<T>
{
public:
    using tuple_type = std::tuple<T>;

    virtual ~IVisitorTs() = default;
    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>());
}
struct A;
struct B;
struct C;
struct D;

using IAVisitor = IVisitorTs<A, B, C, D>;

struct A {
    virtual ~A() = default;
    virtual void accept(IAVisitor& v) const { v.visit(*this); }
};
struct B : A {
    virtual void accept(IAVisitor& v) const override { v.visit(*this); }
};

struct C : A {
    virtual void accept(IAVisitor& v) const override { v.visit(*this); }
};
struct D : A {
    virtual void accept(IAVisitor& v) const override { v.visit(*this); }
};

class Object {
    public:
        virtual double foo (A*, A*) { std::cout << "Object::foo A,A\n";  return 3.14; }
        virtual double foo (B*, B*) { std::cout << "Object::foo B,B\n";  return 3.14; }
        virtual double foo (B*, C*) { std::cout << "Object::foo B,C\n";  return 3.14; }
        virtual double foo (C*, B*) { std::cout << "Object::foo C,B\n";  return 3.14; }
        virtual double foo (C*, C*) { std::cout << "Object::foo C,C\n";  return 3.14; }
        virtual char foo (A*, A*, A*) const { std::cout << "Object::foo A,A,A\n";  return '&'; }
        virtual char foo (C*, B*, D*) const { std::cout << "Object::foo C,B,D\n";  return '!'; }  // Overload of foo with three arguments.
        virtual void bar (A*, A*, A*) const { std::cout << "Object::bar A,A,A\n"; }
        virtual void bar (B*, B*, B*) const { std::cout << "Object::bar B,B,B\n"; }
        virtual void bar (B*, C*, B*) const { std::cout << "Object::bar B,C,B\n"; }
        virtual void bar (B*, C*, C*) const { std::cout << "Object::bar B,C,C\n"; }
        virtual void bar (B*, C*, D*) const { std::cout << "Object::bar B,C,D\n"; }
        virtual void bar (C*, B*, D*) const { std::cout << "Object::bar C,B,D\n"; }
        virtual void bar (C*, C*, C*) const { std::cout << "Object::bar C,C,C\n"; }
        virtual void bar (D*, B*, C*) const { std::cout << "Object::bar D,B,C\n"; }
        double fooMultipleDispatch (A*, A*);
        char fooMultipleDispatch (A*, A*, A*);
};

class FooDispatcher
{
public:
    explicit FooDispatcher(Object& object) : object(object) {}

    template <typename T1, typename T2>
    double operator() (T1& a1, T2& a2) const
    {
        return object.foo(&a1, &a2);
    }

    template <typename T1, typename T2, typename T3>
    char operator() (T1& a1, T2& a2, T3& a3) const
    {
        return object.foo(&a1, &a2, &a3);
    }
private:
    Object& object;
};

double Object::fooMultipleDispatch (A* a1, A* a2)
{
    return dispatch<double, IAVisitor>(FooDispatcher(*this), *a1, *a2);
}
char Object::fooMultipleDispatch (A* a1, A* a2, A* a3)
{
    return dispatch<char, IAVisitor>(FooDispatcher(*this), *a1, *a2, *a3);
}


int main() {
    A a_a;
    B a_b;
    C a_c;
    D a_d;
    A* a[] = {&a_b, &a_c, &a_d, &a_a};
    Object object;

    double d = object.foo (a[0], a[1]);  // Object::foo A,A  (no multiple dispatch)
    d = object.fooMultipleDispatch (a[0], a[1]);  // Object::foo B,C
    std::cout << "d = " << d << std::endl;  // 3.14

    object.fooMultipleDispatch (a[0], a[3]);  // B,A -> so best match is Object::foo A,A

    const char k = object.fooMultipleDispatch (a[1], a[0], a[2]);  // Object::foo C,B,D
    std::cout << "k = " << k << std::endl;  // !
}
#include <iostream>
#include <tuple>
#include <functional>
#include <utility>
#include <array>

class A {
public:
    virtual ~A() = default;
    virtual std::size_t getIndex() const = 0;
};

template <typename Derived>
class CRTP : public A {
    virtual std::size_t getIndex() const override;
};

class B : public CRTP<B> {};

class C : public CRTP<C> {};

class D : public CRTP<D> {};

class Object {
    public:
        double foo (A*, A*) { std::cout << "Object::foo A,A\n";  return 3.14; }
        double foo (B*, B*) { std::cout << "Object::foo B,B\n";  return 3.14; }
        double foo (B*, C*) { std::cout << "Object::foo B,C\n";  return 3.14; }
        double foo (C*, B*) { std::cout << "Object::foo C,B\n";  return 3.14; }
        double foo (C*, C*) { std::cout << "Object::foo C,C\n";  return 3.14; }
        double foo (B*, D*) { std::cout << "Object::foo B,D\n";  return 3.14; }
        char foo (A*, A*, A*) const { std::cout << "Object::foo A,A,A\n";  return '&'; }
        char foo (C*, B*, D*) const { std::cout << "Object::foo C,B,D\n";  return '!'; }
        template <typename R, typename... Args> R fooMultipleDispatch (Args*...);
};

using ArgumentTuple = std::tuple<A,B,C,D>;  // This must be updated whenever the Object::foo overloads are updated with new argument types.

template <std::size_t N>
using ArgumentType = std::tuple_element_t<N, ArgumentTuple>;

template <std::size_t N, typename Arg>
using Type = Arg;

template <typename T, std::size_t N, T v>
constexpr T Value = v;

template <typename T, typename... Ts> struct GetIndex;

template <typename T, typename... Ts>
struct GetIndex<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};

template <typename T, typename Tail,  typename... Ts>
struct GetIndex<T, Tail, Ts...> : std::integral_constant <std::size_t, GetIndex<T, Ts...>::value + 1> {};

template <typename T, typename Tuple> struct GetIndexInTuple;

template <typename T, template <typename...> class Tuple, typename... Ts>
struct GetIndexInTuple<T, Tuple<Ts...>> : GetIndex<T, Ts...> {};

template <typename Derived>
std::size_t CRTP<Derived>::getIndex() const {
    return GetIndexInTuple<Derived, ArgumentTuple>::value;
}

template <typename Element, typename Array>
auto getArrayElement (const Element& element, const Array&, std::size_t) {
    return element;
}

template <typename Subarray, std::size_t N, typename Array>
auto getArrayElement (const std::array<Subarray, N>& array, const Array& indices, std::size_t n = 0) {
    return getArrayElement(array[indices[n]], indices, n+1);
}

template <typename... Ts>
constexpr std::array<std::common_type_t<Ts...>, sizeof...(Ts)> makeArray (Ts&&... ts) {
    return { {std::forward<Ts>(ts)...} };
}

template <typename R, typename Class, typename Arg, std::size_t... Is>  // This terminating createMultiArrayHelper overload just creates one of the foo overloads.
constexpr auto createMultiArrayHelper (std::index_sequence<>, std::index_sequence<Is...>) {
    return [](Class* object, Type<Is, Arg*>... args)->R {return object->foo(static_cast<ArgumentType<Is>*>(args)...);};
}

template <typename R, typename Class, typename Arg, std::size_t First, std::size_t... Rest, std::size_t... Is, std::size_t... Js>
constexpr auto createMultiArrayHelper (std::index_sequence<Is...>, std::index_sequence<Js...>) {  // sizeof...(Is) is the dimension size of the current subarray.
    return makeArray (createMultiArrayHelper<R, Class, Arg, Rest...>(std::make_index_sequence<First>{}, std::index_sequence<Js..., Is>{})...);
}

template <typename R, typename Class, typename Arg, std::size_t First, std::size_t... Rest>
constexpr auto createMultiArray() {
    return createMultiArrayHelper<R, Class, Arg, Rest..., 0>(std::make_index_sequence<First>{}, std::index_sequence<>{});  // 0 for the terminating case, since std::make_index_sequence<0> is std::index_sequence<> (in the terminating createMultiArrayHelper overload).
}

template <typename R, typename Class, typename Arg, std::size_t Size, std::size_t... Is>
constexpr auto createFunctionMultiArrayHelper (std::index_sequence<Is...>) {
    return createMultiArray<R, Class, Arg, Value<std::size_t, Is, Size>...>();  // i.e. createMultiArray<R, Class, Arg, Size, Size, Size, ..., Size>(), where Size is repeated sizeof...(Is) times.
}

template <typename R, typename Class, typename Arg, std::size_t Size, std::size_t NumDimensions>
constexpr auto createFunctionMultiArray() {
    return createFunctionMultiArrayHelper<R, Class, Arg, Size>(std::make_index_sequence<NumDimensions>{});
}

template <typename R, typename... Args>
R Object::fooMultipleDispatch (Args*... args) {  // Jarod42 says that R must be known during compile-time (as his solution requires as well), though covariant return types can be possible if Args*... is B*,B*,...
    constexpr std::size_t N = sizeof...(Args);
    static const auto fooOverloads = createFunctionMultiArray<R, Object, A, std::tuple_size<ArgumentTuple>::value, N>();  // We use the keyword static because we only want to create this (const) array once for the entire program run.
    const std::array<std::size_t, N> indexArray = {args->getIndex()...};
    const auto f = getArrayElement(fooOverloads, indexArray);
    return f(this, args...);
}

int main() {
    B* b = new B;
    C* c = new C;
    D* d = new D;
    A* a[] = {b, c, d};
    Object object;
    object.foo (a[0], a[2]);  // Object::foo A,A  (no multiple dispatch)
    object.fooMultipleDispatch<double>(a[0], a[2]);  // Object::foo B,D  (multiple dispatch!)
    object.fooMultipleDispatch<char>(a[1], a[0], a[2]);  // Object::foo C,B,D  (multiple dispatch!)
}