C++:std::optional

1059 篇文章 285 订阅

引言

在介绍之前,我们从一个问题出发,C++ 的函数如何返回多个值?

比较有年代感的一种做法是将返回值作为引用参数传入,函数的返回值用来标识运行状态,比如像下面这样

#include <iostream>

using namespace std;

int func(const string& in, string& out1, string& out2) {
    if (in.size() == 0)
        return 0;
    out1 = "hello";
    out2 = "world";
    return 1;
}

int main() {
    string out1, out2;
    int status = func("hi", out1, out2);
    if (status) {
        cout << out1 << endl;
        cout << out2 << endl;
    }
    return 0;
}

这种做法性能不错,但可读性会比较差,参数列表里既包含了入参也包含了出参,常见通过变量名前缀来标识,尤其是在出入参比较多的时候,后期维护会非常头疼。

在 C++ 11 中新增了 tuple 这种数据结构的支持,自然也可以使用 tuple 来实现多个返回值

#include <iostream>
#include <tuple>

using namespace std;

tuple<bool, string, string> func(const string& in) {
    if (in.size() == 0)
        return make_tuple(false, "", "");
    return make_tuple(true, "hello", "world");
}

int main() {
    if (auto [status, out1, out2] = func("hi"); status) {
        cout << out1 << endl;
        cout << out2 << endl;
    }
    return 0;
}

上面这段代码中的 auto [status, out1, out2] = func("hi"); 是 C++ 17 中叫 Structured Bindings 的新特性,效果就是将多个返回值按照顺序绑定到方括号中的变量名中。

tuple 在这里用起来不是很爽的地方是需要刻意的记忆每个返回值的位置,在返回值数量比较多的时候就会带来比较大的困扰,返回值的语意表达的。

还有一种做法就是将函数返回值定义成一个结构体,同时要返回函数的运行状态,我们可以考虑把这两部分数据定义成一个 pair ,pair 可以理解为一种特殊的 tuple(只有 2 个元素的 tuple)。

#include <iostream>

using namespace std;

struct Out {
    string out1 { "" };
    string out2 { "" };
};

pair<bool, Out> func(const string& in) {
    Out o;
    if (in.size() == 0)
        return { false, o };
    o.out1 = "hello";
    o.out2 = "world";
    return { true, o };
}

int main() {
    if (auto [status, o] = func("hi"); status) {
        cout << o.out1 << endl;
        cout << o.out2 << endl;
    }
    return 0;
}

在线运行

目前这种做法可以做到让返回值更富有语意,并且可以很方便的扩展,如果要增加一个新的返回值,只需要扩展现有的结构体就可以了。
最后这种做法中的 pair<bool, Out> 这个数据结构实现的功能就跟本文要介绍 std::optional 很相似了。

std::optional

From cppreference -std::optional: 类模板 std::optional 管理一个可选的容纳值,即可以存在也可以不存在的值。
一种常见的 optional 使用情况是一个可能失败的函数的返回值。与其他手段,如 std::pair<T,bool> 相比, optional 良好地处理构造开销高昂的对象,并更加可读,因为它显式表达意图。

std::optional 是在 C++ 17 中引入到标准库中的,C++ 17 之前的版本可以通过 boost::optional 实现几乎相同的功能。

我们来看一下使用 std::optional 来实现上面那段代码的样子

#include <iostream>
#include <optional>

using namespace std;

struct Out {
    string out1 { "" };
    string out2 { "" };
};

optional<Out> func(const string& in) {
    Out o;
    if (in.size() == 0)
        return nullopt;
    o.out1 = "hello";
    o.out2 = "world";
    return { o };
}

int main() {
    if (auto ret = func("hi"); ret.has_value()) {
        cout << ret->out1 << endl;
        cout << ret->out2 << endl;
    }
    return 0;
}

这段代码中我们看到了部分 std::optional 的用法,std::nullopt 是 C++ 17 中提供的没有值的 optional 的表达形式,等同于 { } 。

创建一个 optional 的方法:

// 空 optiolal
optional<int> oEmpty;
optional<float> oFloat = nullopt;

optional<int> oInt(10);
optional oIntDeduced(10);  // type deduction

// make_optional
auto oDouble = std::make_optional(3.0);
auto oComplex = make_optional<complex<double>>(3.0, 4.0);

// in_place
optional<complex<double>> o7{in_place, 3.0, 4.0};

// initializer list
optional<vector<int>> oVec(in_place, {1, 2, 3});

// 拷贝赋值
auto oIntCopy = oInt;

访问 optional 对象中数据的方法:

// 跟迭代器的使用类似,访问没有 value 的 optional 的行为是未定义的
cout << (*ret).out1 << endl; 
cout << ret->out1 << endl;

// 当没有 value 时调用该方法将 throws std::bad_optional_access 异常
cout << ret.value().out1 << endl;

// 当没有 value 调用该方法时将使用传入的默认值
Out defaultVal;
cout << ret.value_or(defaultVal).out1 << endl;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值