c++ – Collider class using some kind of visitor pattern

I’m trying to create a Collider class which will process collisions between different classes of the same base. One restriction is that I want these classes know nothing about each other, now they depend on Base and Collider classes only. Here is my code:

collidees.h

    #pragma once

    #include "collider.h"

    #define ALLOW_COLLIDER_VISIT                                        
        virtual void visit(Collider& c) override { c.collide(*this); }; 
        virtual void visit(Collider& c, Base& other) override { c.collide(*this, other); };

    struct Base {
        virtual ~Base() = default;

        virtual void visit(Collider& c)              = 0;
        virtual void visit(Collider& c, Base& other) = 0;
    };

    struct A : Base {
        ALLOW_COLLIDER_VISIT;
    };

    struct B : Base {
        ALLOW_COLLIDER_VISIT;
    };

    struct C : Base {
        ALLOW_COLLIDER_VISIT;
    };

collider.h

    #pragma once

    #include <memory>
    #include <iostream>

    #define ADD_REVERSE_COLLISION(A, B)                    
        template<>                                         
        inline void Collider::performCollision(B& b, A& a) 
        {                                                  
            performCollision(a, b);                        
        }

    #define CALL_COLLIDER_FOR(CLASS) 
        void collide(CLASS& a) { collider.performCollision<T, CLASS>(static_cast<T&>(base), a); }

    struct Base;
    struct A;
    struct B;
    struct C;
    struct Collider;

    struct HelperBase {
        HelperBase(Base& base_, Collider& collider_) : base { base_ }, collider { collider_ } {}
        virtual ~HelperBase() = default;

        virtual void collide(A& base) = 0;
        virtual void collide(B& base) = 0;
        virtual void collide(C& base) = 0;

        Base&     base;
        Collider& collider;
    };

    template<typename T>
    struct Helper;

    struct Collider {
        template<typename T>
        void collide(T& t) {
            if (helper) {
                helper->collide(t);
            }
        }

        template<typename T>
        void collide(T& t, Base& other) {
            helper = std::make_unique<Helper<T>>(t, *this);
            callOtherToVisit(other);
        }

        void callOtherToVisit(Base& other);

        template<typename T1, typename T2>
        void performCollision(T1& first, T2& second) {
            std::cout << "No collision handlern";
        }

        std::unique_ptr<HelperBase> helper = nullptr;
    };

    template<typename T>
    struct Helper : HelperBase {
        Helper(T& base, Collider& c) : HelperBase { base, c } {}

        CALL_COLLIDER_FOR(A);
        CALL_COLLIDER_FOR(B);
        CALL_COLLIDER_FOR(C);
    };

    template<>
    inline void Collider::performCollision(A& a, B& b) {
        std::cout << "Colliding a and bn";
    }
    ADD_REVERSE_COLLISION(A, B);

    template<>
    inline void Collider::performCollision(B& b1, B& b2) {
        std::cout << "Colliding b1 and b2n";
    }

    template<>
    inline void Collider::performCollision(A& a, C& c) {
        std::cout << "Colliding a and cn";
    }
    ADD_REVERSE_COLLISION(A, C);

    template<>
    inline void Collider::performCollision(B& b, C& c) {
        std::cout << "Colliding b and cn";
    }
    ADD_REVERSE_COLLISION(B, C);

collider.cpp

    #include "collider.h"
    #include "collidees.h"

    void Collider::callOtherToVisit(Base& other)
    {
        other.visit(*this);
    }

main.cpp - driver code

    #include "collidees.h"
    #include "collider.h"

    int main()
    {
        Collider collider;

        auto a = std::make_unique<A>();
        auto b = std::make_unique<B>();
        auto c = std::make_unique<C>();

        a->visit(collider, *b);
        a->visit(collider, *c);
        c->visit(collider, *b);
        c->visit(collider, *a);
        b->visit(collider, *a);

        return 0;
    }

I suppose there are some disadvantages of this code:

  1. It’s quite complicated; adding new collidee entails adding code in multiple places;
  2. There is one static_cast insted of true dispatching (although, I suppose it is safe);
  3. It’s possible to call void visit(Collider& c) yourself, what is meaningless;
  4. Class Helper stores reference to the object that can be easily deleted, which causes storing of dangling reference.

(Feel free to add other drawbacks in comments)

I would be glad to see any suggestions and improvements (or even your versions of this code).