c++ - 如何创建共享概念的对象向量?
问题描述
我想创建一个包含不同类型但都共享相同概念的对象的向量(或数组)。
类似于Vec<Box<dyn trait>>
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;
}
解决方案
您要查找的内容通常称为“类型擦除”。C++20concept
的语言特性不(也不能)支持类型擦除——该特性完全限于约束模板(类模板、函数模板、类模板的成员函数等),不能真正用于任何其他语境。
您必须改为Talk
手动编写可擦除的类型,或者求助于使用可用的类型擦除库之一。
例如,使用dyno(Louis Dionne 在CppCon 2017和CppNow 2018上进行了几次演讲),看起来如下。你会注意到我使用这个概念的唯一地方Talk
是约束默认的概念图:
#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();
}
}
有关 C++ 类型擦除的其他资源,另请参阅:
- Sean Parent 的Inheritance 是 Base Class of Evil和C++ Seasoning的演讲,无论如何每个人都应该看到。这演示了一种执行运行时多态性的机制。
- Sy Brand 的带有元类的动态多态性(他们在多个会议上发表了这个演讲,最近一次是ACCU 2021)。这演示了如何使用反射工具编写类型擦除库,从而使整体代码比我上面展示的要少得多。
推荐阅读
- migration - 我将数据导出到通用消息传递的尝试创建了太多打开的连接并且服务器因 OutOfMemoryError 异常而关闭
- c# - 使用 Json.NET,如何在序列化对象时加密任何类型的选定属性?
- c++ - SFML 和 Box2D 坐标同步
- google-chrome - 为什么 XHR 在 Google Chrome DevTools 中显示 js 加载请求
- javascript - 在 redux reducer 中破坏默认状态
- java - 为什么 Java 正在寻找 java.lang.NoClassDefFoundError: com/mongodb/client/MongoClients?
- unix - 在文件 foo 中,如何使用 grep 定位并打印包含 printf 的所有行,但不包括 sprintf 和 fprintf,但在行尾的任何位置?
- c# - Oracle View 上的 EF Core 查询非常慢
- python - 四元数衍生的旋转矩阵问题
- python - 有没有办法加速这个 Python 脚本?