做流量网站要做哪一种,公司网站建设457216336,做家政有专门的网站吗,免费做logo的网站相关文章系列 C三剑客之std::optional(一) : 使用详解 C三剑客之std::any(一) : 使用 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)#xff1a;深入剖析 目录
1.概述
2.构建方式
2.1.默认构造
2.2.移动构造
2.3.拷贝构… 相关文章系列 C三剑客之std::optional(一) : 使用详解 C三剑客之std::any(一) : 使用 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)深入剖析 目录
1.概述
2.构建方式
2.1.默认构造
2.2.移动构造
2.3.拷贝构造
2.4.std::in_place_t构造
2.5.std::make_optional构造
3.修改器
3.1.operator
3.2.emplace
3.3.reset
3.4.swap
4.观察器
4.1.operator-和operator*
4.2.operator bool和has_value
4.3.value
4.4.value_or
5.单子操作(C23起)
5.1.and_then
5.2.transform
5.3.or_else
6.使用场景
7.注意事项
7.1.std::optional未初始化去访问
7.2.std::optional的比较操作
7.3.std::optional的生命周期
8.总结 1.概述 C17的三剑客分别是std::optional, std::any, std::vairant。今天主要讲std::optional。它是一个类模版在头文件optional中定义定义如下
template class T
class optional; 类模板 std::optional 管理一个可选 的容纳值既可以存在也可以不存在的值。 一种常见的 optional 使用情况是一个可能失败的函数的返回值。与其他手段如 std::pairT, bool 相比optional 良好地处理构造开销高昂的对象并更加可读因为它显式表达意图。 以下是一个简单的代码示例展示了如何在C程序中引入和使用std::optional
#include optional // 引入std::optional
#include iostreamstd::optionaldouble getValue(bool r) {if (r) {return 1.52;} else {return std::nullopt;}
}int main() {auto value getValue(true);if (value.has_value()) {std::cout Value: *value std::endl;} else {std::cout No value std::endl;}return 0;
}在这个代码示例中我们首先引入了optional头文件然后定义了一个返回std::optionaldouble的函数getValue。在main函数中我们调用了getValue函数并使用has_value成员函数检查返回值是否存在。如果存在我们使用解引用运算符*来获取值。
2.构建方式
std::optional的构建方式主要有 2.1.默认构造
std::optional的默认构造函数创建一个不包含值的std::optional对象。这在你需要延迟初始化或者表示一个可能不存在的值时非常有用。
std::optionaldouble opt; // 创建一个不包含值的std::optional对象
2.2.移动构造
移动构造又称右值构造。你可以通过提供一个右值来构造std::optional对象。这个值将被复制或移动到新创建的std::optional对象中。如下示例
std::optionaldouble a(10.09); // 创建一个包含值10.09的std::optional对象
std::optionaldouble b(std::move(a)); //使用移动构造函数创建一个新的std::optional对象
2.3.拷贝构造
如下示例
std::optionalint a(22222);
std::optionalint b(a); // 使用拷贝构造函数创建一个新的std::optional对象
2.4.std::in_place_t构造
std::optional类还提供了in-place构造函数允许你在std::optional对象的存储空间中直接构造值避免了不必要的拷贝或移动操作。std::in_place是消除歧义的标签其传递给ystd::optional的构造函数用来指示原位构造对象。示例如下
#include iostream
#include optional
#include stringint main()
{// 调用 std::string( initializer_listCharT ) 构造函数std::optionalstd::string o4(std::in_place, {a, b, c});// 调用 std::string( size_type count, CharT ch ) 构造函数std::optionalstd::string o5(std::in_place, 3, A);// 从 std::string 移动构造用推导指引拾取类型std::optional o6(std::string{deduction});std::cout *o2 *o3 *o4 *o5 *o6 \n;
}
输出1 1 abc AAA deduction
2.5.std::make_optional构造
创建一个包含给定值的std::optional对象它的定义如下 从定义看出可以从右值可变参数和std::initializer_list等多种方式用std::make_optional构造出std::optional对象。示例如下
#include optional
#include iostream
#include iomanip
#include vector
#include stringint main()
{auto op1 std::make_optionalstd::vectorchar({a,b,c});std::cout op1: ;for (char c: op1.value()){std::cout c ,;}auto op2 std::make_optionalstd::vectorint(5, 2);std::cout \nop2: ;for (int i: *op2){std::cout i ,;}std::string str{hello world};auto op3 std::make_optionalstd::string(std::move(str));std::cout \nop3: quoted(op3.value_or(empty value)) \n;std::cout str: std::quoted(str) \n;
}
输出
op1: a,b,c,
op2: 2,2,2,2,2,
op3: hello world
str:
3.修改器
3.1.operator
对内容赋值重载operator操作符的类型有 从中看出std::optional的赋值函数参数包括std::nullopt_t、左值引用、右值引用、模板单值、模板做值和模板右值。示例如下
#include optional
#include iostream
int main()
{std::optionalconst char* s1 abc, s2; // 构造函数s2 s1; // 赋值s1 def; // 衰变赋值 U char[4], T const char* std::cout *s2 *s1 \n;
}
输出abc def
3.2.emplace
emplace的定义如下 示例如下
#include optional
#include iostreamstruct 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::optionalint a;a.emplace(10); // 在optional对象中就地构造一个值std::optionalA 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;
}
输出
Assign:constructedmove constructeddestructed
Emplace:destructedconstructed
End exampledestructed
3.3.reset
重置对象若 std::optional 含值则如同用 value().T::~T() 销毁此值。否则无效果。示例如下
#include optional
#include iostreamstruct 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::optionalA 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;
}
输出
Create empty optional:
Construct and assign value:constructedmove constructeddestructed
Reset optional:destructed
End example
3.4.swap
交换内容如果内部有值这先析构内部值再交换值。示例如下
#include iostream
#include string
#include optionalint main()
{std::optionalstd::string opt1(First example text);std::optionalstd::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);
}
输出
Before swap:
opt1 contains First example text
opt2 contains 2nd text
---SWAP---
After swap:
opt1 contains 2nd text
opt2 contains First example textBefore 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.
4.观察器
4.1.operator-和operator*
operator-返回所含值的指针operator*返回所函数的引用 此运算符不检查 std::optional 是否含值你能手动用 has_value() 或简单地用 operator bool() 做检查。另外若需要有检查访问可使用 value() 或 value_or() 。 示例如下
#include optional
#include iostream
#include stringint main()
{using namespace std::string_literals;std::optionalint opt1 1;std::cout opt1: *opt1 \n;*opt1 2;std::cout opt1: *opt1 \n;std::optionalstd::string opt2 abcs;std::cout opt2: *opt2 size: opt2-size() \n;// 你能通过在到 optional 的右值上调用 operator* “取”其所含值auto taken *std::move(opt2);std::cout taken: taken opt2: *opt2 size: opt2-size() \n;
}
输出
opt1: 1
opt1: 2
opt2: abc size: 3
taken: abc opt2: size: 0
4.2.operator bool和has_value
检查std::optional是否函数这个比较简单这里就不赘述了。
4.3.value
若 std::optional含值则返回到所含值引用示例如下
#include optional
#include iostream
int main()
{std::optionalint opt {};try {int n opt.value();} catch(const std::exception e) {std::cout e.what() \n;}
}
输出bad optional access
4.4.value_or
value_or的定义如下
若std::optional 拥有值则返回其所含的值否则返回 default_value 。
1) 等价于 bool(*this) ? **this : static_castT(std::forwardU(default_value))
2) 等价于 bool(*this) ? std::move(**this) : static_castT(std::forwardU(default_value))
示例如下
#include optional
#include iostream
#include cstdlibstd::optionalconst char* maybe_getenv(const char* n)
{if(const char* x std::getenv(n))return x;elsereturn {};
}
int main()
{std::cout maybe_getenv(MYPWD).value_or((none)) \n;
}
输出(none)
5.单子操作(C23起)
5.1.and_then
在所含值存在时返回对其应用给定的函数的结果否则返回空的 std::optional。下面示例可能包括C23的部分内容不清楚的地方可以去查询相关文档。代码如下
#include charconv
#include iomanip
#include iostream
#include optional
#include ranges
#include string
#include string_view
#include vectorstd::optionalint to_int(std::string_view sv)
{int r {};auto [ptr, ec] { std::from_chars(sv.data(), sv.data() sv.size(), r) };if (ec std::errc())return r;elsereturn std::nullopt;
}int main()
{using namespace std::literals;const std::vectorstd::optionalstd::string v{1234, 15 foo, bar, 42, 5000000000, 5, std::nullopt, -43};for (auto x : v | std::views::transform([](auto o){// 调试打印输入的 optionalstring 的内容std::cout std::left std::setw(13) std::quoted(o.value_or(nullopt)) - ;return o// 若 optional 为空则转换它为持有 字符串的 optional.or_else([]{ return std::optional{s}; })// 拉平映射 string 为 int 失败时产生空的 optional.and_then(to_int)// 映射 int 为 int 1.transform([](int n) { return n 1; })// 转换回 string.transform([](int n) { return std::to_string(n); })// 以 and_than 替换并用 NaN 变换并忽略所有剩余的空 optional// and_then and ignored by transforms with NaN.value_or(NaNs);}))std::cout x \n;
}
输出
1234 - 1235
15 foo - 16
bar - NaN
42 - 43
5000000000 - NaN5 - NaN
nullopt - NaN
-43 - -42
5.2.transform
在所含值存在时返回含有变换后的所含值的 std::optional否则返回空的 std::optional。示例如下
#include iostream
#include optionalstruct A { /* ... */ };
struct B { /* ... */ };
struct C { /* ... */ };
struct D { /* ... */ };auto A_to_B(A) - B { /* ... */ std::cout A B \n; return {}; }
auto B_to_C(B) - C { /* ... */ std::cout B C \n; return {}; }
auto C_to_D(C) - D { /* ... */ std::cout C D \n; return {}; }void try_transform_A_to_D(std::optionalA o_A)
{std::cout (o_A ? o_A has a value\n : o_A is empty\n);std::optionalD o_D o_A.transform(A_to_B).transform(B_to_C).transform(C_to_D);std::cout (o_D ? o_D has a value\n\n : o_D is empty\n\n);
};int main()
{try_transform_A_to_D( A{} );try_transform_A_to_D( {} );
}
输出
o_A has a value
A B
B C
C D
o_D has a valueo_A is empty
o_D is empty
5.3.or_else
在 std::optional 含值时返回自身否则返回给定函数的结果。功能比较简单在这里就不在赘述了。
6.使用场景 函数返回值当函数可能返回一个值也可能不返回值时可以使用std:optional作为返回类型。这种方式可以避免使用指针或特殊值来表示无值的情况从而提高代码的简洁性和安全性。 参数传递将std::optional作为数参数可以接受或忽略该参数的值。这种方式可以使函数更加灵活适应不同的情况。 容器类可以使用std:optional作为容器类如std:vector、std:list等的元素类型以存储可能不存在的值。这种方式可以方便地处理容器中的空值而无需使用指针或特殊值。 可选状态当某个对象可能处于某种状态也可能不处于该状态时可以使用std:optional来表示该状态。例如一个购物车可能包含一个可选的运费可以使用std::optionaldouble来表示是否计算运费。 异步编程在异步编程中std:optional可以用于表示异步操作的结果。例如一个异步函数可能返回一个std::optionalint表示异步计算的结果可能是一个整数值或者没有结果空值。 7.注意事项
在使用C的std::optional类时有一些重要的注意事项需要我们了解。这些注意事项可以帮助我们更好地理解和使用std::optional类避免在编程中出现错误。
7.1.std::optional未初始化去访问
当我们创建一个std::optional对象但没有给它赋值时这个对象就处于未初始化的状态。在这种状态下如果我们试图访问它的值就会抛出std::bad_optional_access异常。
std::optionalint a;
try {int value a.value(); // 抛出std::bad_optional_access
} catch (const std::bad_optional_access e) {std::cout e.what() \n;
}
在这个例子中我们创建了一个未初始化的std::optional对象并试图访问它的值。这会抛出一个std::bad_optional_access异常我们可以捕获这个异常并处理它。
在实际编程中我们应该在访问std::optional的值之前先使用has_value()函数或者bool运算符检查它是否已经被初始化。
std::optionalint a;
if (a) { // 或者 if (a.has_value())int value a.value();
}
7.2.std::optional的比较操作
std::optional支持所有的比较操作包括, !, , , , 。这些比较操作首先会比较两个std::optional对象的初始化状态然后再比较它们的值。
std::optionalint a 1;
std::optionalint b 2;
std::optionalint b;std::cout (a b) \n; // 输出0因为a和b的值不相等
std::cout (a c) \n; // 输出0因为a已经初始化而c未初始化
std::cout (c std::nullopt) \n; // 输出1因为c未初始化
在这个例子中我们创建了两个已经初始化的std::optional对象和一个未初始化的std::optional对象然后比较它们的值和初始化状态。
7.3.std::optional的生命周期
std::optional的生命周期和它包含的值的生命周期是一致的。当std::optional被销毁时它包含的值也会被销毁。这意味着我们不能返回一个包含局部变量的std::optional。
std::optionalstd::string getName(bool c) {
std::string name zdxiao;
if (c) {return name; // 错误返回一个包含局部变量的std::optional
}
return std::nullopt;在这个例子中我们试图返回一个包含局部变量name的std::optionalstd::string。但是当getName函数返回时name变量会被销毁所以返回的std::optionalstd::string会包含一个已经被销毁的值。
在实际编程中我们应该避免返回包含局部变量的std::optional。我们可以返回一个值或者返回std::nullopt_t表示没有值。
std::optionalstd::string getName(bool c) {
if (c) {return zdxiao; // 正确返回一个值
}return std::nullopt; // 正确表示没有值
}
在这个修改后的例子中我们返回一个字符串字面量而不是一个局部变量。这样返回的std::optionalstd::string就会包含一个有效的值。
以上就是在使用std::optional类时需要注意的一些重要事项。在实际编程中我们应该充分理解和掌握这些注意事项以避免在编程中出现错误。
8.总结
上面全面讲解了std::optional的用法和一些注意事项要想深入理解它那就需要在平时的工作中慢慢的去使用它细细体会才能真正领会发明std::optional的意义。
参考std::optional - cppreference.com