Files
SH-Quizzes/ACMOJ-2096.hpp
2023-12-23 22:23:48 +08:00

138 lines
4.0 KiB
C++

#pragma once
#include <new>
#include <stdexcept>
#include <utility>
// 你不可以使用任何其他的头文件
namespace koishi {
/**
* @brief 一个可以存储任意类型的类 (确信)。
* 其要么存储了某个类型的值,要么为空(不存储任何值)。
*/
class any {
private:
// 你不可以额外添加任何成员变量。
// base 类的实现在 any 类的尾部。
// 你需要通过观察 base 和 storage 类的实现来完成 any 类的实现。
// 以下是 base 类和 storage 类的实现。
// 你可以思考一下为什么要这么设计。
// 事实上 any 类的设计并不唯一,标准库也并不是这么写的
// 标准库使用函数指针那种写法,我觉得还是这种写法比较好理解些。
struct base {
virtual ~base() = default;
virtual base *clone() const = 0;
};
template <typename T>
struct storage : base {
T value; // 真实的数据存储在这里哦
// 完美转发,接受任意类型的参数
// 现在 storage 有 T 所有的构造函数了
template <typename Args>
storage(Args &&args) : value(std::forward<Args>(args)) {}
// 拷贝一份自己,返回基类的指针。
base *clone() const override { return new storage(value); }
};
base *ptr = nullptr; // 指向基类的指针
public:
// 默认构造函数,创建一个空的 any 对象。
any() = default;
// 你同样可以使用 nullptr 来创建一个空的 any 对象。
any(std::nullptr_t) : any() {}
// 销毁当前对象。
// 重申: delete nullptr 是安全的。
~any() { delete ptr; }
// 拷贝构造函数,创建一个和 other 一样的 any 对象。
// 可能要用到 clone() 函数
any(const any &other) {
if (other.ptr) ptr = other.ptr->clone();
}
// 移动构造函数,创建一个和 other 一样的 any 对象。
// 你只需要移走 other 的数据,使得 other 为空即可。
// 你不应该用到 clone() 函数
any(any &&other) {
ptr = other.ptr;
other.ptr = nullptr;
}
// 创建一个存储了某个类型的值的 any 对象。
// 你不用管模板的第二个参数。那是用来约束 T ,保证 T 不是 any 类型的。
template <typename T,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, any>>>
any(const T &val) {
ptr = new storage<T>(val);
}
// 拷贝赋值运算符,要求类似拷贝构造函数。
any &operator=(const any &other) {
if (ptr) delete ptr;
ptr = nullptr;
if (other.ptr) ptr = other.ptr->clone();
return *this;
}
// 移动赋值运算符,要求类似移动构造函数。
any &operator=(any &&other) {
if (ptr) delete ptr;
ptr = other.ptr;
other.ptr = nullptr;
return *this;
}
// 强行把当前对象转换成 T 类型。
// 如果当前存储的不是 T 类型的值,那么抛出异常 std::bad_cast
template <typename T>
T &cast() {
storage<T> *new_ptr = dynamic_cast<storage<T> *>(ptr);
if (new_ptr != nullptr)
return new_ptr->value;
else
throw std::bad_cast();
}
// 差不多,但是是 const 版本的。
template <typename T>
const T &cast() const {
storage<T> *new_ptr = dynamic_cast<storage<T> *>(ptr);
if (new_ptr != nullptr)
return new_ptr->value;
else
throw std::bad_cast();
}
// 尝试把当前对象转换成 T 类型。
// 如果当前存储的不是 T 类型的值,那么返回 nullptr
template <typename T>
T *try_cast() {
storage<T> *new_ptr = dynamic_cast<storage<T> *>(ptr);
if (new_ptr != nullptr)
return &new_ptr->value;
else
return nullptr;
}
// 差不多,但是是 const 版本的。
template <typename T>
const T *try_cast() const {
storage<T> *new_ptr = dynamic_cast<storage<T> *>(ptr);
if (new_ptr != nullptr)
return &new_ptr->value;
else
return nullptr;
}
// 判断当前对象是否为空。
bool is_empty() const { return ptr == nullptr; }
};
} /* namespace koishi */