自己实现一个std::optional
扫描二维码
随时随地手机看文章
前面我写过一篇推荐使用 std::optional 的文章,而 optional 是C++17 引入的新特性,很多读者反馈自己的项目还停留在C++11标准,没办法使用C++17,也就没办法使用 optional,既然不能直接使用 std::optional,我们可以考虑自己实现一个。
其实optional的功能比较明确,它允许变量可能包含值也可能不包含值。
下面我将逐步解释如何自己实现一个简单的 optional 类:
nullopt_t 结构体
struct nullopt_t { struct init {}; constexpr explicit nullopt_t(init) {}};constexpr nullopt_t nullopt{nullopt_t::init()};
这里定义一个nullopt_t,nullopt_t 是一个特殊类型,用来表示 optional 对象中没有值。我们定义了一个 init 结构体来确保 nullopt_t 的构造函数是显式的,避免不必要的隐式转换。
optional 类的构造函数
template <typename T>class optional {public: optional() : has_value_(false) {} optional(nullopt_t) : has_value_(false) {} optional(const T& value) : has_value_(true), value_(value) {} optional(T&& value) : has_value_(true), value_(std::move(value)) {}};
这些构造函数初始化 optional 对象。optional() 和 optional(nullopt_t) 构造函数将 has_value_ 设置为 false,表示没有值。其余两个构造函数分别接受左值引用和右值引用,初始化 value_ 并将 has_value_ 设置为 true。
拷贝和移动构造函数
optional(const optional& other) : has_value_(other.has_value_), value_(other.value_) {}optional(optional&& other) : has_value_(other.has_value_), value_(std::move(other.value_)) {}
拷贝构造函数和移动构造函数用于创建一个新的 optional 对象,它们分别从另一个 optional 对象中拷贝或移动值。
拷贝和移动赋值运算符
optional& operator=(const optional& other) { if (this != &other) { has_value_ = other.has_value_; value_ = other.value_; } return *this;} optional& operator=(optional&& other) { if (this != &other) { has_value_ = other.has_value_; value_ = std::move(other.value_); } return *this;}
赋值运算符用于将一个 optional 对象的值赋给另一个 optional 对象。拷贝赋值运算符从另一个 optional 对象中拷贝值,而移动赋值运算符则移动值方便提高效率。
nullopt_t 赋值运算符和普通赋值运算符
optional& operator=(nullopt_t) { has_value_ = false; return *this;} optional& operator=(const T& value) { has_value_ = true; value_ = value; return *this;} optional& operator=(T&& value) { has_value_ = true; value_ = std::move(value); return *this;}
这些赋值运算符允许我们将 nullopt 或一个具体值赋给 optional 对象。
检查值是否存在的方法
bool has_value() const { return has_value_; }
has_value 方法返回一个布尔值,表示 optional 对象是否包含值。
获取值的方法
T& value() { if (!has_value_) { throw std::exception(); } return value_;} const T& value() const { if (!has_value_) { throw std::exception(); } return value_;}
value 方法返回 optional 对象中的值,如果没有值则抛出异常。
返回默认值的方法
T value_or(const T& default_value) const { return has_value_ ? value_ : default_value;}
value_or 方法返回 optional 对象中的值,如果没有值则返回默认值。
重载操作符
T& operator()() { return value(); } const T& operator()() const { return value(); } optional& swap(optional& other) { std::swap(has_value_, other.has_value_); std::swap(value_, other.value_); return *this;} T* operator->() { return &value(); } const T* operator->() const { return &value(); } T& operator*() { return value(); } const T& operator*() const { return value(); }
这里又重载了一些操作符,这些使 optional 对象的使用更加便捷。operator() 和 operator* 使得我们可以像使用普通对象一样使用 optional 对象。operator-> 提供了指针语法的支持。swap 方法交换两个 optional 对象的值。
私有成员变量
private: bool has_value_; T value_;};
has_value_ 是一个布尔值,用来表示 optional 对象是否包含值。value_ 存储实际的值。
测试代码
int main() { optional<int> opt1; optional<int> opt2 = 42; optional<int> opt3 = nullopt; std::cout << "opt1 has value: " << opt1.has_value() << std::endl; std::cout << "opt2 has value: " << opt2.has_value() << std::endl; std::cout << "opt3 has value: " << opt3.has_value() << std::endl; if (opt2.has_value()) { std::cout << "opt2 value: " << opt2.value() << std::endl; } opt3 = 10; std::cout << "opt3 value after assignment: " << opt3.value() << std::endl; opt3 = nullopt; std::cout << "opt3 has value after assigning nullopt: " << opt3.has_value() << std::endl; try { std::cout << "opt1 value: " << opt1.value() << std::endl; } catch (const std::exception& e) { std::cout << "Exception caught: opt1 has no value" << std::endl; } optional<int> opt4 = opt2; std::cout << "opt4 value after copy construction: " << opt4.value() << std::endl; optional<int> opt5 = std::move(opt2); std::cout << "opt5 value after move construction: " << opt5.value() << std::endl; optional<int> opt6; opt6 = opt4; std::cout << "opt6 value after copy assignment: " << opt6.value() << std::endl; optional<int> opt7; opt7 = std::move(opt4); std::cout << "opt7 value after move assignment: " << opt7.value() << std::endl; optional<int> opt8 = 5; std::cout << "opt8 value before swap: " << opt8.value() << std::endl; opt8.swap(opt7); std::cout << "opt8 value after swap: " << opt8.value() << std::endl; std::cout << "opt7 value after swap: " << opt7.value() << std::endl; std::cout << "opt6 value_or test: " << opt6.value_or(100) << std::endl; std::cout << "opt3 value_or test: " << opt3.value_or(100) << std::endl; optional<std::string> optStr = std::string("Hello, optional!"); if (optStr.has_value()) { std::cout << "optStr value: " << *optStr << std::endl; } return 0;}
在这个测试代码中,展示了如何使用我们实现的 optional 类。下面是每个测试部分的简要说明:
-
默认构造和空值初始化:
optional<int> opt1;optional<int> opt2 = 42;optional<int> opt3 = nullopt;
创建三个optional 对象,其中一个无值,一个初始化为 42,一个初始化为 nullopt。
-
检查值是否存在:
std::cout << "opt1 has value: " << opt1.has_value() << std::endl;std::cout << "opt2 has value: " << opt2.has_value() << std::endl;std::cout << "opt3 has value: " << opt3.has_value() << std::endl;
使用 has_value() 方法检查每个 optional 对象是否包含值。
-
获取值:
if (opt2.has_value()) { std::cout << "opt2 value: " << opt2.value() << std::endl;}
在确保 optional 对象包含值后,使用 value() 方法获取值。
-
赋值操作:
opt3 = 10;std::cout << "opt3 value after assignment: " << opt3.value() << std::endl; opt3 = nullopt;std::cout << "opt3 has value after assigning nullopt: " << opt3.has_value() << std::endl;
展示了如何将值和 nullopt 赋给 optional 对象。
-
异常处理:
try { std::cout << "opt1 value: " << opt1.value() << std::endl;} catch (const std::exception& e) { std::cout << "Exception caught: opt1 has no value" << std::endl;}
如果尝试获取一个没有值的 optional 对象的值,会抛出异常。
-
拷贝和移动构造:
optional<int> opt4 = opt2;std::cout << "opt4 value after copy construction: " << opt4.value() << std::endl; optional<int> opt5 = std::move(opt2);std::cout << "opt5 value after move construction: " << opt5.value() << std::endl;
展示了如何通过拷贝和移动构造函数创建 optional 对象。
-
拷贝和移动赋值:
optional<int> opt6;opt6 = opt4;std::cout << "opt6 value after copy assignment: " << opt6.value() << std::endl; optional<int> opt7;opt7 = std::move(opt4);std::cout << "opt7 value after move assignment: " << opt7.value() << std::endl;
展示了如何通过拷贝和移动赋值操作符将值赋给 optional 对象。
-
交换值:
optional<int> opt8 = 5;std::cout << "opt8 value before swap: " << opt8.value() << std::endl;opt8.swap(opt7);std::cout << "opt8 value after swap: " << opt8.value() << std::endl;std::cout << "opt7 value after swap: " << opt7.value() << std::endl;
-
默认值获取:
std::cout << "opt6 value_or test: " << opt6.value_or(100) << std::endl;std::cout << "opt3 value_or test: " << opt3.value_or(100) << std::endl;
使用 value_or 方法获取 optional 对象的值,如果没有值则返回默认值。
10. 字符串类型的 optional:
optional<std::string> optStr = std::string("Hello, optional!");if (optStr.has_value()) { std::cout << "optStr value: " << *optStr << std::endl;}
创建一个 optional 对象,并检查其值。
总结
通过上述代码和讲解,我们展示了如何实现一个简单的 optional 类。希望本文能帮助你更好地理解 std::optional 的实现原理。
下面为完整源代码
struct nullopt_t { struct init {}; constexpr explicit nullopt_t(init) {}}; constexpr nullopt_t nullopt{nullopt_t::init()}; template <typename T>class optional { public: optional() : has_value_(false) {} optional(nullopt_t) : has_value_(false) {} optional(const T& value) : has_value_(true), value_(value) {} optional(T&& value) : has_value_(true), value_(std::move(value)) {} optional(const optional& other) : has_value_(other.has_value_), value_(other.value_) {} optional(optional&& other) : has_value_(other.has_value_), value_(std::move(other.value_)) {} optional& operator=(const optional& other) { if (this != &other) { has_value_ = other.has_value_; value_ = other.value_; } return *this; } optional& operator=(optional&& other) { if (this != &other) { has_value_ = other.has_value_; value_ = std::move(other.value_); } return *this; } optional& operator=(nullopt_t) { has_value_ = false; return *this; } optional& operator=(const T& value) { has_value_ = true; value_ = value; return *this; } optional& operator=(T&& value) { has_value_ = true; value_ = std::move(value); return *this; } bool has_value() const { return has_value_; } T& value() { if (!has_value_) { throw std::exception(); } return value_; } const T& value() const { if (!has_value_) { throw std::exception(); } return value_; } T value_or(const T& default_value) const { return has_value_ ? value_ : default_value; } T& operator()() { return value(); } const T& operator()() const { return value(); } optional& swap(optional& other) { std::swap(has_value_, other.has_value_); std::swap(value_, other.value_); return *this; } T* operator->() { return &value(); } const T* operator->() const { return &value(); } T& operator*() { return value(); } const T& operator*() const { return value(); } private: bool has_value_; T value_;};





