I want to create a vector (or array) of objects of different types but all sharing the same concept.
similar to Vec<Box<dyn trait>>
of Rust.
struct Dog {
void talk() {
std::cout << "guau guau" << std::endl;
}
};
struct Cat {
void talk() {
std::cout << "miau miau" << std::endl;
}
};
template <typename T>
concept Talk = requires(T a) {
{ a.talk() } -> std::convertible_to<void>;
};
int main() {
auto x = Dog{};
auto y = Cat{};
??? pets = {x, y};
for(auto& pet: pets) {
pet.talk();
}
return 0;
}
What you're looking for is typically called "type erasure." The C++20 concept
s language feature does not (and cannot) support type erasure - that feature is limited entirely to constraining templates (class templates, function templates, member functions of class templates, etc.) and cannot really be used in any other context.
You'll have to instead either write your type erased Talk
able by hand or resort to using one of the available type erasure libraries.
For example, with dyno (which Louis Dionne gave several talks on CppCon 2017, CppNow 2018), this would look as follows. You'll note that the only place I'm using the concept Talk
is to constrain the default concept map:
#include <dyno.hpp>
#include <vector>
#include <iostream>
using namespace dyno::literals;
// this is the "concept" we're going to type erase
struct PolyTalkable : decltype(dyno::requires_(
dyno::CopyConstructible{},
dyno::Destructible{},
"talk"_s = dyno::method<void()>
)) { };
template <typename T>
concept Talk = requires (T a) { a.talk(); };
// this how we implement our "concept"
template <Talk T>
auto const dyno::default_concept_map<PolyTalkable, T> = dyno::make_concept_map(
"talk"_s = [](T& self) { self.talk(); }
);
// this is our hand-written "dyn PolyTalkable"
class DynTalkable {
dyno::poly<PolyTalkable> impl_;
public:
template <typename T>
requires (!std::same_as<T, DynTalkable>
&& dyno::models<PolyTalkable, T>())
DynTalkable(T t) : impl_(t) { }
void talk() {
impl_.virtual_("talk"_s)();
}
};
struct Dog {
void talk() {
std::cout << "guau guau" << std::endl;
}
};
struct Cat {
void talk() {
std::cout << "miau miau" << std::endl;
}
};
int main() {
std::vector<DynTalkable> pets;
pets.push_back(Dog{});
pets.push_back(Cat{});
for (auto& pet : pets) {
pet.talk();
}
}
For other resources on C++ type erasure, see also:
You can’t create a vector of different unrelated types. This kind of situation is generally handled using a polymorphic base class rather than using concepts, eg:
struct Animal {
virtual void talk() = 0;
};
struct Dog : Animal {
void talk() override {
std::cout << "guau guau" << std::endl;
}
};
struct Cat : Animal {
void talk() override {
std::cout << "miau miau" << std::endl;
}
};
int main() {
auto x = Dog{};
auto y = Cat{};
std::vector<Animal*> pets{&x, &y};
for(auto& pet : pets) {
pet->talk();
}
return 0;
}
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With