|
|
|
|
|
by pjmlp
471 days ago
|
|
Here is an example then, assuming you mean this kind of abstrations, #include <iostream>
using namespace std;
template <typename T>
concept Speaker = requires (T t) {
t.speak();
};
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<Speaker T>
void speaking_animal(const T& animal) {
animal.speak();
cout << "\n\n";
}
template<Speaker... 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);
}
Live example, https://godbolt.org/z/vPhf13xEh |
|
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.
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.
animal_api is a regular function, which represents some external API that we don't get to recompile.