【cpp】std::optional

原文地址

Intro

float divide(float a, float b)
{
	if (b == 0)
	{
		return ?;
	}
	return a / b;
}

这里以一个除法函数为例,当 b 为 0 的时候,明显是除法的异常,但是怎样把这个状态返回给调用方呢?

常见的方法有如下几种

  • 抛异常,当除0时抛出相关的异常,调用方捕获异常
  • 返回一个特殊的值 (-1, infinity, nullptr) 这种需要和调用方约定好,语义不是很清晰。
  • Optional types. 这是一种类型安全并且表达力更强。

std::optional 在 c++17中被加入,以前的c++标准可以通过 boost::optional 来获取。在c++17中可以使用#include 来使用这个类型。

经过 std::optional 封装的仍然是一个值类型,可以拷贝。 std::optional不需要在堆上申请内存。

When to use

通常,可能在这些场景使用 std::optional封装

  • 需要较好的表示空类型 (nullable type)
    • 替代使用特殊值 (-1, nullptr, NO_VALUE 或者其他)
    • 比如某些值可能存在可能不存在,但是我们又需要知道是否存在, 比如,在统计年龄的时候,可能有的数据里面没有年龄,这个时候我们可以使用std::optional表示。
  • 返回一个执行失败的结果
    • 比如除0 错误,不能得到正确的值的时候。
  • 懒加载资源的时候
    • 比如,一个 resource 类型没有默认的构造函数,但是构造的工作 比较庞大,可以使用 std::optional来定义,然后在需要的时候来加载。
    • 在传参的时候

Basic Example

std::optional<std::string> UI::FindUserNick()
{
    if (nick_available)
        return { mStrNickName };

    return std::nullopt; // same as return { };
}

// use:
std::optional<std::string> UserNick = UI->FindUserNick();
if (UserNick)
    Show(*UserNick);

Creation

// empty:
std::optional<int> oEmpty;
std::optional<float> oFloat = std::nullopt;

// direct:
std::optional<int> oInt(10);
std::optional oIntDeduced(10); // deduction guides

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

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

// will call vector with direct init of {1, 2, 3}
std::optional<std::vector<int>> oVec(std::in_place, {1, 2, 3});

// copy/assign:
auto oIntCopy = oInt;

获取值

// by operator*
std::optional<int> oint = 10;
std::cout<< "oint " << *opt1 << '\n';

// by value()
std::optional<std::string> ostr("hello");
try
{
    std::cout << "ostr " << ostr.value() << '\n';  
}
catch (const std::bad_optional_access& e)
{
    std::cout << e.what() << "\n";
}

// by value_or()
std::optional<double> odouble; // empty
std::cout<< "odouble " << odouble.value_or(10.0) << '\n';

一种稳妥的做法时在访问的时候判断是否为空

    // compute string function:
    std::optional<std::string> maybe_create_hello();  
    // ...  

    if (auto ostr = maybe_create_hello(); ostr)
        std::cout << "ostr " << *ostr << '\n';  
    else  
        std::cout << "ostr is null\n";

修改值

#include 
#include 
#include 

class UserName
{
public:
    explicit UserName(const std::string& str) : mName(str)
    { 
        std::cout << "UserName::UserName(\'";
        std::cout << mName << "\')\n"; 
    }
    ~UserName() 
    {
        std::cout << "UserName::~UserName(\'";
        std::cout << mName << "\')\n"; 
    }

private:
    std::string mName;
};

int main()
{
    std::optional<UserName> oEmpty;

    // emplace:
    oEmpty.emplace("Steve");

    // calls ~Steve and creates new Mark:
    oEmpty.emplace("Mark");


    // reset so it's empty again
    oEmpty.reset(); // calls ~Mark
    // same as:
    //oEmpty = std::nullopt;

    // assign a new value:
    oEmpty.emplace("Fred");
    oEmpty = UserName("Joe"); 
}

你可能感兴趣的:(cpp,c++)