前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++那些事之玩转optional

C++那些事之玩转optional

作者头像
公众号guangcity
发布2023-09-02 10:39:12
3000
发布2023-09-02 10:39:12
举报
文章被收录于专栏:光城(guangcity)

C++那些事之玩转optional

0.导语

本节将会引入5个版本的optional实现,最终揭秘C++ STL optional实现,最后给出一个小项目作为练习的例子,让大家感受step by step学习的乐趣,所有代码、答案与相关参考资料,已更新于知识星球,欢迎大家加入学习。

1.引入

C++17之后,C++标准库提供了std::optional,它是一个管理可选包含值的类模板。可选类型或有时也称为Maybe类型表示可选值的封装。

The class template std::optional manages an optional contained value, i.e. a value that may or may not be present.

cppreference:

https://en.cppreference.com/w/cpp/utility/optional

那么在C++17之前自己实现的话,如何造一个std::optional?

2.简易版optional

第一个版本是比较简单的,我们引入bool变量来标记当前类模版是否办函值。

代码语言:javascript
复制
template <typename T>
class Optional {
private:
  bool hasValue;
  T value;
};

在使用的地方:

代码语言:javascript
复制
if (optionalInt.hasValue()) {
} else {
}

仔细想想这样子写有什么问题呢?

  1. 内存对齐开销,增加了bool+padding的开销。
  2. 构造T对象的开销,例如:T无效时,是没有必要的。

那么如何优化呢?

3.指针版optional

代码语言:javascript
复制
template <typename T>
class Optional {
private:
  std::unique_ptr<T> value;
};

调用处:

代码语言:javascript
复制
if (val) {
   // has value
} else {
   // nullptr
}

这个虽然解决了上述的问题,却引发了下面几个问题:

  1. 内存开销:std::unique_ptr使用了堆内存来存储实际值。这意味着每个可选类型对象都需要额外的堆内存分配,这可能会导致内存开销增加。
  2. 不能存储空值:std::unique_ptr要求始终持有一个有效的指针,因此无法表示空值。如果你需要表示一个可选类型的空值状态,你可能需要引入其他的标志来表示空值状态。

对于第二点,给个示例,当直接获取数据是,此时应该预期返回空值,而不是nullptr。

代码语言:javascript
复制
// 报错:空指针
std::cout << "Value: " << optionalInt.getData() << std::endl;

4.pair版optional

对于前面几个版本都有点问题,继续优化,我们不用unique_ptr,回到第一个版本,干掉bool变量不就解决了一开始的内存对齐问题吗。

代码语言:javascript
复制
template <typename T>
class Optional {
private:
  T data;
};

在返回数据的时候带上bool标记不就行了。

代码语言:javascript
复制
std::pair<bool, T> getData() const {
  if (con) {
   return std::make_pair(true, data);
  } else {
    return std::make_pair(false, data);
  }
}

调用的时候我们通过first、second这种方式语义太不明确了,接口不够清晰。

代码语言:javascript
复制
otherOptionalInt.getData().first 
otherOptionalInt.getData().second 

5.storage版optional

我们希望可选对象默认情况下不构造包含的对象。实现它的一种方法是使用std::aligned_storage为所包含的对象保留空间,随后用placement new,即使用new运算符在现有位置构造一个对象。

代码语言:javascript
复制
template <typename T>
class Optional {
 private:
  using StorageType =
      typename std::aligned_storage<sizeof(T), alignof(T)>::type;

  StorageType data;
  bool hasValue;
};

6.union版optional

在C++ stl中实现为:

代码语言:javascript
复制
struct _Empty_byte { };
union _Storage
{
 _Empty_byte _M_empty;
 _Up _M_value;
};

使用处:

代码语言:javascript
复制
_Storage<_Stored_type> _M_payload;

我们对这个做一层抽象,变为了当前的union版本实现:

代码语言:javascript
复制
struct _Empty_byte { };

template <typename T>
class Optional {
private:
  union {
    T value;
    _Empty_byte empty;
  };
  bool hasValue;
}

这下跟stl实现差不多了,缺了一些继承/封装等等。

7.小项目:字符串转数字

给大家布置一个课堂练习,壳子为,请补充完代码。

代码语言:javascript
复制
inline std::string trim(const std::string& s)
{

}
template<typename T = int>
std::optional<T> stonum(const std::string& st)
{
  
}

// 调用
int main()
{
 string num;

 num = "n1";
 if (auto n = stonum(num); n.has_value())
  cout << num << " is integer " << *n << endl;
 else
  cout << num << " is not an integer" << endl;

 num = "42z";
 if (auto n = stonum(num); n.has_value())
  cout << num << " is integer " << *n << endl;
 else
  cout << num << " is not an integer" << endl;
}

本节抽丝剥茧,一层层的揭开optional的实现,欢迎大家转发支持。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++那些事之玩转optional
  • 0.导语
  • 1.引入
  • 2.简易版optional
  • 3.指针版optional
  • 4.pair版optional
  • 5.storage版optional
  • 6.union版optional
  • 7.小项目:字符串转数字
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档