C++ std::optional 详解

发布于:2025-02-26 ⋅ 阅读:(15) ⋅ 点赞:(0)

概述

  1. C++17 模板类
  2. The class template std::optional manages an optional contained value, i.e. a value that may or may not be present.
    1. 类模板std::optional管理一个可选的包含值,即一个可能存在也可能不存在的值。
  3. A common use case for optional is the return value of a function that may fail. As opposed to other approaches, such as std::pair<T, bool>, optional handles expensive-to-construct objects well and is more readable, as the intent is expressed explicitly.
    1. optional的一个常见用例是可能失败的函数的返回值。与其他方法(例如std::pair<T, bool>)相比,optional能很好地处理构造代价高昂的对象,并且更具可读性,因为其意图被明确表达了出来。
  4. Any instance of optional at any given point in time either contains a value or does not contain a value.
    1. 任何时候的任何optional实例要么包含一个值,要么不包含一个值。
  5. If an optional contains a value, the value is guaranteed to be nested within the optional object. Thus, an optional object models an object, not a pointer, even though operator*() and operator->() are defined.
    1. 如果一个optional包含一个值,那么这个值肯定是嵌套在optional对象内部的。因此,optional对象模拟的是一个对象,而非一个指针,即便解引用运算符*()和箭头运算符->()已被定义。
  6. The optional object is a view that contains either one element if it contains a value, or otherwise zero elements if it does not contain a value. The lifetime of the contained element is bound to the object.
    1. optional对象是一个view,如果它包含一个值,则包含一个元素;否则,如果它不包含值,则包含零个元素。所包含元素的生命周期与该对象绑定。
#include <iostream>
#include <optional>
#include <string>
 
// optional can be used as the return type of a factory that may fail
std::optional<std::string> create(bool b)
{
    if (b)
        return "Godzilla";
    return {};
}
 
// std::nullopt can be used to create any (empty) std::optional
auto create2(bool b)
{
    return b ? std::optional<std::string>{"Godzilla"} : std::nullopt;
}
 
int main()
{
    std::cout << "create(false) returned "
              << create(false).value_or("empty") << '\n';
 
    // optional-returning factory functions are usable as conditions of while and if
    if (auto str = create2(true))
        std::cout << "create2(true) returned " << *str << '\n';
}

// output
create(false) returned empty
create2(true) returned Godzilla

Member functions

构造函数

在这里插入图片描述

默认构造

std::optional<double> opt; // 创建一个不包含值的std::optional对象

移动构造

std::optional<double> a(10.09); // 创建一个包含值10.09的std::optional对象
std::optional<double> b(std::move(a)); //使用移动构造函数创建一个新的std::optional对象

拷贝构造

std::optional<int> a(22222);
std::optional<int> b(a); // 使用拷贝构造函数创建一个新的std::optional对象

make_optional

#include <iomanip>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
 
int main()
{
    auto op1 = std::make_optional<std::vector<char>>({'a','b','c'});
    std::cout << "op1: ";
    for (char c : op1.value())
        std::cout << c << ',';
    auto op2 = std::make_optional<std::vector<int>>(5, 2);
    std::cout << "\nop2: ";
    for (int i : *op2)
        std::cout << i << ',';
    std::string str{"hello world"};
    auto op3 = std::make_optional<std::string>(std::move(str));
    std::cout << "\nop3: " << std::quoted(op3.value_or("empty value")) << '\n';
    std::cout << "str: " << std::quoted(str) << '\n';
}

// output
op1: a,b,c,
op2: 2,2,2,2,2,
op3: "hello world"
str: ""

operator=

在这里插入图片描述

#include <optional>
#include <iostream>
int main()
{
    std::optional<const char*> s1 = "abc", s2; // 构造函数
    s2 = s1; // 赋值
    s1 = "def"; // 衰变赋值( U = char[4], T = const char* )
    std::cout << *s2 << ' ' << *s1 << '\n';
}
// output
abc def

Observers

operator->和operator*
  1. operator->返回所含值的指针;operator*返回所函数的引用
  2. 此运算符不检查 std::optional 是否含值,
    1. 需要手动用 has_value() 或简单地用 operator bool() 做检查。另外,若需要有检查访问,可使用 value() 或 value_or()
#include <iomanip>
#include <iostream>
#include <optional>
#include <string>
 
int main()
{
    using namespace std::string_literals;
 
    std::optional<int> opt1 = 1;
    std::cout << "opt1: " << *opt1 << '\n';
 
    *opt1 = 2;
    std::cout << "opt1: " << *opt1 << '\n';
 
    std::optional<std::string> opt2 = "abc"s;
    std::cout << "opt2: " << std::quoted(*opt2) << ", size: " << opt2->size() << '\n';
 
    // You can "take" the contained value by calling operator* on an rvalue to optional
 
    auto taken = *std::move(opt2);
    std::cout << "taken: " << std::quoted(taken) << "\n"
                 "opt2: " << std::quoted(*opt2) << ", size: " << opt2->size()  << '\n';
}

// output
opt1: 1
opt1: 2
opt2: "abc", size: 3
taken: "abc"
opt2: "", size: 0
operator bool和has_value
  1. Checks whether *this contains a value.
#include <optional>
#include <iostream>
 
int main()
{
    std::cout << std::boolalpha;
 
    std::optional<int> opt;
    std::cout << opt.has_value() << '\n';
 
    opt = 43;
    if (opt)
        std::cout << "value set to " << opt.value() << '\n';
    else
        std::cout << "value not set\n";
 
    opt.reset();
    if (opt.has_value())
        std::cout << "value still set to " << opt.value() << '\n';
    else
        std::cout << "value no longer set\n";
}

// output
false
value set to 43
value no longer set
value
  1. 若 std::optional含值,则返回到所含值引用
#include <iostream>
#include <optional>
 
int main()
{
    std::optional<int> opt = {};
 
    try
    {
        [[maybe_unused]] int n = opt.value();
    }
    catch(const std::bad_optional_access& e)
    {
        std::cout << e.what() << '\n';
    }
 
    try
    {
        opt.value() = 42;
    }
    catch(const std::bad_optional_access& e)
    {
        std::cout << e.what() << '\n';
    }
 
    opt = 43;
    std::cout << *opt << '\n';
 
    opt.value() = 44;
    std::cout << opt.value() << '\n';
}

// output
bad optional access
bad optional access
43
44
value_or
  1. Returns the contained value if *this contains a value, otherwise returns default_value.
#include <cstdlib>
#include <iostream>
#include <optional>
 
std::optional<const char*> maybe_getenv(const char* n)
{
    if (const char* x = std::getenv(n))
        return x;
    else
        return {};
}
 
int main()
{
    std::cout << maybe_getenv("SHELL").value_or("(none)") << '\n';
    std::cout << maybe_getenv("MYPWD").value_or("(none)") << '\n';
}

// output
/usr/bin/zsh
(none)

Modifiers

swap
  1. 交换内容,如果内部有值,这先析构内部值,再交换值。示例如下:
#include <iostream>
#include <string>
#include <optional>
 
int main()
{
    std::optional<std::string> opt1("First example text");
    std::optional<std::string> opt2("2nd text");
 
    enum Swap { Before, After };
    auto print_opts = [&](Swap e) {
        std::cout << (e == Before ? "Before swap:\n" : "After swap:\n");
        std::cout << "opt1 contains '" << opt1.value_or("") << "'\n";
        std::cout << "opt2 contains '" << opt2.value_or("") << "'\n";
        std::cout << (e == Before ? "---SWAP---\n": "\n");
    };
 
    print_opts(Before);
    opt1.swap(opt2);
    print_opts(After);
 
    // 在仅一者含值时交换
    opt1 = "Lorem ipsum dolor sit amet, consectetur tincidunt.";
    opt2.reset();
 
    print_opts(Before);
    opt1.swap(opt2);
    print_opts(After);
}

// output
Before swap:
opt1 contains 'First example text'
opt2 contains '2nd text'
---SWAP---
After swap:
opt1 contains '2nd text'
opt2 contains 'First example text'
 
Before swap:
opt1 contains 'Lorem ipsum dolor sit amet, consectetur tincidunt.'
opt2 contains ''
---SWAP---
After swap:
opt1 contains ''
opt2 contains 'Lorem ipsum dolor sit amet, consectetur tincidunt.'
reset
  1. 重置对象,若 std::optional 含值,则如同用 value().T::~T() 销毁此值。否则无效果。示例如下:
#include <optional>
#include <iostream>
 
struct A {
    std::string s;
    A(std::string str) : s(std::move(str))  { std::cout << " constructed\n"; }
    ~A() { std::cout << " destructed\n"; }
    A(const A& o) : s(o.s) { std::cout << " copy constructed\n"; }
    A(A&& o) : s(std::move(o.s)) { std::cout << " move constructed\n"; }
    A& operator=(const A& other) {
        s = other.s;
        std::cout << " copy assigned\n";
        return *this;
    }
    A& operator=(A&& other) {
        s = std::move(other.s);
        std::cout << " move assigned\n";
        return *this;
    }
};
 
int main()
{
    std::cout << "Create empty optional:\n";
    std::optional<A> opt;
 
    std::cout << "Construct and assign value:\n";
    opt = A("Lorem ipsum dolor sit amet, consectetur adipiscing elit nec.");
 
    std::cout << "Reset optional:\n";
    opt.reset();
    std::cout << "End example\n";
}

// output
Create empty optional:
Construct and assign value:
 constructed
 move constructed
 destructed
Reset optional:
 destructed
End example
emplace

在这里插入图片描述

#include <optional>
#include <iostream>
 
struct A {
    std::string s;
    A(std::string str) : s(std::move(str))  { std::cout << " constructed\n"; }
    ~A() { std::cout << " destructed\n"; }
    A(const A& o) : s(o.s) { std::cout << " copy constructed\n"; }
    A(A&& o) : s(std::move(o.s)) { std::cout << " move constructed\n"; }
    A& operator=(const A& other) {
        s = other.s;
        std::cout << " copy assigned\n";
        return *this;
    }
    A& operator=(A&& other) {
        s = std::move(other.s);
        std::cout << " move assigned\n";
        return *this;
    }
};
 
int main()
{
    std::optional<int> a;
    a.emplace(10); // 在optional对象中就地构造一个值
 
    std::optional<A> opt;
 
    std::cout << "Assign:\n";
    opt = A("Lorem ipsum dolor sit amet, consectetur adipiscing elit nec.");
 
    std::cout << "Emplace:\n";
    // 由于 opt 含值,这亦将销毁该值
    opt.emplace("Lorem ipsum dolor sit amet, consectetur efficitur. ");
 
    std::cout << "End example\n";
}

// output
Assign:
 constructed
 move constructed
 destructed
Emplace:
 destructed
 constructed
End example
 destructed

参考文章

std::optional - cppreference.com
C++三剑客之std::optional(一) : 使用详解_c++ optional-CSDN博客


网站公告

今日签到

点亮在社区的每一天
去签到