电子产业一站式赋能平台

PCB联盟网

搜索
查看: 121|回复: 0
收起左侧

手把手带你实现一个C++17的新特性 std::optional

[复制链接]
匿名  发表于 2024-8-9 09:00:00 |阅读模式
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() {    optionalint> opt1;    optionalint> opt2 = 42;    optionalint> opt3 = nullopt;
        std::cout "opt1 has value: " std::endl;    std::cout "opt2 has value: " std::endl;    std::cout "opt3 has value: " std::endl;
        if (opt2.has_value()) {        std::cout "opt2 value: " std::endl;    }
        opt3 = 10;    std::cout "opt3 value after assignment: " std::endl;
        opt3 = nullopt;    std::cout "opt3 has value after assigning nullopt: " std::endl;
        try {        std::cout "opt1 value: " std::endl;    } catch (const std::exception& e) {        std::cout "Exception caught: opt1 has no value" std::endl;    }
        optionalint> opt4 = opt2;    std::cout "opt4 value after copy construction: " std::endl;
        optionalint> opt5 = std::move(opt2);    std::cout "opt5 value after move construction: " std::endl;
        optionalint> opt6;    opt6 = opt4;    std::cout "opt6 value after copy assignment: " std::endl;
        optionalint> opt7;    opt7 = std::move(opt4);    std::cout "opt7 value after move assignment: " std::endl;
        optionalint> opt8 = 5;    std::cout "opt8 value before swap: " std::endl;    opt8.swap(opt7);    std::cout "opt8 value after swap: " std::endl;    std::cout "opt7 value after swap: " std::endl;
        std::cout "opt6 value_or test: " 100) std::endl;    std::cout "opt3 value_or test: " 100) std::endl;
        optionalstd::string> optStr = std::string("Hello, optional!");    if (optStr.has_value()) {        std::cout "optStr value: " std::endl;    }
        return 0;}在这个测试代码中,展示了如何使用我们实现的 optional 类。下面是每个测试部分的简要说明:
    默认构造和空值初始化:
    [/ol]
  • optionalint> opt1;optionalint> opt2 = 42;optionalint> opt3 = nullopt;创建三个optional 对象,其中一个无值,一个初始化为 42,一个初始化为 nullopt。
    检查值是否存在:
    [/ol]
  • std::cout "opt1 has value: " std::endl;std::cout "opt2 has value: " std::endl;std::cout "opt3 has value: " std::endl;使用 has_value() 方法检查每个 optional 对象是否包含值。
    获取值:
    [/ol]
  • if (opt2.has_value()) {    std::cout "opt2 value: " value() }在确保 optional 对象包含值后,使用 value() 方法获取值。
    赋值操作:
    [/ol]
  • opt3 = 10;std::cout "opt3 value after assignment: " std::endl;
    opt3 = nullopt;std::cout "opt3 has value after assigning nullopt: " std::endl;展示了如何将值和 nullopt 赋给 optional 对象。
    异常处理:
    [/ol]
  • try {    std::cout "opt1 value: " std::endl;} catch (const std::exception& e) {    std::cout "Exception caught: opt1 has no value" std::endl;}如果尝试获取一个没有值的 optional 对象的值,会抛出异常。
    拷贝和移动构造:
    [/ol]
  • optionalint> opt4 = opt2;std::cout "opt4 value after copy construction: " std::endl;
    optionalint> opt5 = std::move(opt2);std::cout "opt5 value after move construction: " std::endl;展示了如何通过拷贝和移动构造函数创建 optional 对象。
    拷贝和移动赋值:
    [/ol]
  • optionalint> opt6;opt6 = opt4;std::cout "opt6 value after copy assignment: " std::endl;
    optionalint> opt7;opt7 = std::move(opt4);std::cout "opt7 value after move assignment: " std::endl;展示了如何通过拷贝和移动赋值操作符将值赋给 optional 对象。
    交换值:
    [/ol]
  • optionalint> opt8 = 5;std::cout "opt8 value before swap: " std::endl;opt8.swap(opt7);std::cout "opt8 value after swap: " std::endl;std::cout "opt7 value after swap: " std::endl;默认值获取:
    [/ol]
  • std::cout "opt6 value_or test: " 100) std::endl;std::cout "opt3 value_or test: " 100) std::endl;使用 value_or 方法获取 optional 对象的值,如果没有值则返回默认值。
    10. 字符串类型的 optional:
  • optionalstd::string> optStr = std::string("Hello, optional!");if (optStr.has_value()) {    std::cout "optStr value: " std::endl;}创建一个 optional 对象,并检查其值。
    总结
    通过上述代码和讲解,我们展示了如何实现一个简单的 optional 类。希望本文能帮助你更好地理解 std::optional 的实现原理。
    下面为完整源代码
  • #include #include #include
    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_;};
    ——EOF——日常分享C/C++、计算机学习经验、工作体会,欢迎点击此处查看我以前的学习笔记&经验&分享的资源。
    我组建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起进群交流。

    欢迎你添加我的微信,我拉你进技术交流群。此外,我也会经常在微信上分享一些计算机学习经验以及工作体验,还有一些内推机会。


    加个微信,打开另一扇窗
    感谢你的分享,点赞,在看三  

  • 回复

    使用道具

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表