#include <fmt/format.h>
#include <string_view>
#include <variant>
struct SpaceObject {
constexpr virtual ~SpaceObject() = default;
[[nodiscard]] virtual constexpr auto get_name() const noexcept
-> std::string_view = 0;
int x;
int y;
};
struct Craft : SpaceObject {
[[nodiscard]] constexpr std::string_view get_name() const noexcept override {
return "Craft";
}
};
struct Asteroid : SpaceObject {
[[nodiscard]] constexpr std::string_view get_name() const noexcept override {
return "Asteroid";
}
};
std::unique_ptr<SpaceObject> factory();
template <typename... Callable>
struct visitor : Callable... {
using Callable::operator()...;
};
void collide(const Craft &, const Craft &) { std::puts("C/C"); }
void collide(const Asteroid &, const Asteroid &) { std::puts("A/A"); }
void collide(const Craft &, const Asteroid &) { std::puts("C/A"); }
void collide(const Asteroid &, const Craft &) { std::puts("A/C"); }
std::vector<std::variant<Craft, Asteroid>> get_objects();
void process_collisions(const std::variant<Craft, Asteroid> &obj) {
for (const auto &other : get_objects()) {
std::visit(
[](const auto &lhs, const auto &rhs) {
if (lhs.x == rhs.x && lhs.y == rhs.y) {
collide(lhs, rhs);
}
},
obj, other);
}
}
- SpaceObject 基类
struct SpaceObject {
constexpr virtual ~SpaceObject() = default;
[[nodiscard]] virtual constexpr auto get_name() const noexcept
-> std::string_view = 0;
int x;
int y;
};
SpaceObject: 这是一个抽象基类,定义了所有空间对象(如飞船和小行星)共有的特征。
constexpr virtual ~SpaceObject() = default;:
virtual: 指定析构函数是虚函数,确保在删除通过基类指针指向的派生类对象时,会调用派生类的析构函数。
constexpr: 表明这个析构函数可以在编译时被调用,尽管在这个上下文中,它的实际用途较少。constexpr 和 virtual 一起使用是为了支持在编译期和运行期的多态性。
= default;: 告诉编译器生成默认的析构函数实现。
[[nodiscard]] virtual constexpr auto get_name() const noexcept -> std::string_view = 0;:
[[nodiscard]]: 提醒调用者不要忽略返回值。
virtual: 指定 get_name 是一个虚函数,派生类必须实现它。
constexpr: 表示这个函数可以在编译时被计算,前提是它的调用环境允许。
noexcept: 表明这个函数不会抛出异常。
= 0;: 纯虚函数,必须在派生类中实现。
2. Craft 和 Asteroid 派生类
struct Craft : SpaceObject {
[[nodiscard]] constexpr std::string_view get_name() const noexcept override {
return "Craft";
}
};
struct Asteroid : SpaceObject {
[[nodiscard]] constexpr std::string_view get_name() const noexcept override {
return "Asteroid";
}
};
Craft 和 Asteroid: 这两个类继承自 SpaceObject,分别代表飞船和小行星。
get_name: 这两个派生类各自实现了基类的纯虚函数 get_name,返回各自的名称。因为 get_name 是 constexpr 函数,所以它们可以在编译时被调用。
3. visitor 模板
template <typename... Callable>
struct visitor : Callable... {
using Callable::operator()...;
};
visitor 模板: 这个模板类是一个多重继承的工具,用于将多个可调用对象(如 lambda、函数对象等)组合成一个对象。using Callable::operator()…; 语句将所有基类的 operator() 引入当前作用域,从而使 visitor 可以通过一个对象调用多个可调用对象的 operator()。
4. collide 函数
void collide(const Craft &, const Craft &) { std::puts("C/C"); }
void collide(const Asteroid &, const Asteroid &) { std::puts("A/A"); }
void collide(const Craft &, const Asteroid &) { std::puts("C/A"); }
void collide(const Asteroid &, const Craft &) { std::puts("A/C"); }
collide 函数: 这些是处理不同类型对象碰撞的函数重载。当两个 Craft 或两个 Asteroid 碰撞时,分别调用不同的重载函数。对于 Craft 和 Asteroid 的混合碰撞,也有对应的重载函数。
5. process_collisions 函数
std::vector<std::variant<Craft, Asteroid>> get_objects();
void process_collisions(const std::variant<Craft, Asteroid> &obj) {
for (const auto &other : get_objects()) {
std::visit(
[](const auto &lhs, const auto &rhs) {
if (lhs.x == rhs.x && lhs.y == rhs.y) {
collide(lhs, rhs);
}
},
obj, other);
}
}
get_objects(): 假定这个函数返回一个 std::vector,其中包含若干 std::variant<Craft, Asteroid> 对象,即飞船或小行星的列表。
process_collisions 函数:
接受一个 std::variant<Craft, Asteroid> 类型的参数 obj。
使用 for 循环遍历 get_objects() 返回的每一个 std::variant 对象 other。
对于每对 obj 和 other,使用 std::visit 来处理它们的类型并调用适当的 lambda 函数。
Lambda 函数:
lhs 和 rhs 是从 obj 和 other 中提取的对象,可能是 Craft 或 Asteroid。
如果两个对象的位置(x 和 y)相同,则调用相应的 collide 函数处理碰撞。
6. std::visit 的作用
std::visit: 通过传入的两个 std::variant(obj 和 other),std::visit 会自动匹配其中包含的类型,并将其传递给 lambda 函数。lambda 函数根据具体的类型进行处理,这种机制允许在运行时动态地处理多种可能的对象类型。
相较于对于每个元素进行动态转换判断实现了动态dispatch