概述
- C++17 模板类
- The class template
std::optional
manages an optional contained value, i.e. a value that may or may not be present.- 类模板
std::optional
管理一个可选的包含值,即一个可能存在也可能不存在的值。
- 类模板
- 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.optional
的一个常见用例是可能失败的函数的返回值。与其他方法(例如std::pair<T, bool>)相比,optional
能很好地处理构造代价高昂的对象,并且更具可读性,因为其意图被明确表达了出来。
- Any instance of
optional
at any given point in time either contains a value or does not contain a value.- 任何时候的任何optional实例要么包含一个值,要么不包含一个值。
- 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.
- 如果一个optional包含一个值,那么这个值肯定是嵌套在optional对象内部的。因此,optional对象模拟的是一个对象,而非一个指针,即便解引用运算符*()和箭头运算符->()已被定义。
- The
optional
object is aview
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.- 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*
- operator->返回所含值的指针;operator*返回所函数的引用
- 此运算符不检查 std::optional 是否含值,
- 需要手动用 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
- 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
- 若 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
- 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
- 交换内容,如果内部有值,这先析构内部值,再交换值。示例如下:
#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
- 重置对象,若 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博客