Hacker News new | ask | show | jobs
by kazinator 471 days ago
Right, but speaking_animal(x) is not dynamic OOP dispatch; it's a template function that gets instantiated for each animal type.

Moreover, everything here can be done without a concept.

This version of the code builds with g++ -std=c++17. We just get worse diagnostics if we try to use something as a Speaker which doesn't conform.

    #include <iostream>

    using namespace std;

    class Duck {
        public:
        void speak() const {
            cout << "quack";
        }
    };

    class Dog {
        public:
        void speak() const {
            cout << "auau";
        }
    };

    class Cat {
        public:
        void speak() const {
            cout << "miau";
        }
    };

    template<typename T>
    void speaking_animal(const T&  animal) {
        animal.speak();
        cout << "\n\n";
    }

    template<typename... T>
    void speaking_farm(const T&... animals) {
        auto space_adder = [&](auto creature) -> void {
            creature.speak();
            cout << " ";
        };
        (space_adder(animals), ...);
    }


    int main() {
        Duck duck;
        Dog dog;
        Cat cat;

        speaking_animal(duck);
        speaking_animal(dog);
        speaking_animal(cat);
        speaking_farm(duck, dog, cat);
    }
I was thinking about more something along these lines. But note the double indirection: we end up passing the smart pointer animal_pointer by reference.

We achieve the "signature thing" though in that we take these animal objects and effectively get them to to conform to the common animal_pointer abstract base without their cooperation.

    #include <iostream>

    using namespace std;

    class Duck {
    public:
        void speak() const { cout << "quack"; }
    };

    class Dog {
    public:
        void speak() const { cout << "auau"; }
    };

    class Cat {
    public:
        void speak() const { cout << "miau"; }
    };

    class animal_pointer {
    public:
        virtual void speak() const = 0;
    };

    template <typename T> class animal_pointer_impl : public animal_pointer {
    private:
        T *obj;
    public:
        animal_pointer_impl(T *o) : obj(o) { }
        virtual void speak() const { obj->speak(); }
    };

    void animal_api(const animal_pointer &p)
    {
        p.speak();
 cout << '\n';
    }

    int main() {
        Duck duck;
        Dog dog;
        Cat cat;

        animal_pointer_impl<Duck> p0(&duck);
        animal_pointer_impl<Dog> p1(&dog);
        animal_pointer_impl<Cat> p2(&cat);

        animal_api(p0);
        animal_api(p1);
        animal_api(p2);
    }
animal_api is a regular function, which represents some external API that we don't get to recompile.
1 comments

You missed speaking_farm().