admin管理员组文章数量:1027639
C++23中std::optional和std::expected的单子式操作
一、引言
在C++编程中,错误处理和可选值的管理是非常重要的部分。C++17引入了std::optional
,它提供了一种新的标准方式来表达可能缺失的值。而C++23在此基础上,不仅对std::optional
进行了扩展,还引入了std::expected
,并且为它们都提供了受函数式编程启发的新接口,特别是单子式操作(transform、or_else与and_then),这些操作可以简化代码并提高代码的可读性和可维护性。
二、std::optional和std::expected的基本概念
2.1 std::optional
std::optional<T>
是一个类模板,它管理一个可选的容纳值,即这个值既可以存在也可以不存在。一种常见的使用情况是作为一个可能失败的函数的返回值。与其他手段,如std::pair<T, bool>
相比,std::optional
能更好地处理构造开销高昂的对象,并且更加可读,因为它显式表达了意图。例如:
#include <optional> // 引入std::optional
#include <iostream>
std::optional<double> 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;
}
在这个例子中,getValue
函数可能返回一个double
值,也可能返回std::nullopt
表示没有值。在main
函数中,我们使用has_value
成员函数检查返回值是否存在,如果存在则使用解引用运算符*
来获取值。
2.2 std::expected
std::expected<T, E>
是C++23标准库中的新成员,旨在提供一种类型安全的方式来表示可能成功或失败的操作结果。它将有效结果或错误封装在单个对象内,本质上表示类型为T
的预期值或者类型为E
的意外错误。与传统的错误处理技术(如异常或错误码)不同,std::expected
避免了异常带来的复杂性和开销,使得编写健壮软件时更加方便。例如:
#include <expected>
#include <iostream>
#include <string>
enum class parse_error {
invalid_input,
overflow
};
auto parse_number(std::string_view& str) -> std::expected<double, parse_error> {
// 解析逻辑
// 如果解析失败,返回相应的错误
return std::unexpected(parse_error::invalid_input);
// 如果解析成功,返回预期的值
return 3.14;
}
int main() {
std::string_view input = "3.14";
auto result = parse_number(input);
if (result) {
std::cout << "Parsed number: " << *result << std::endl;
} else {
switch (result.error()) {
case parse_error::invalid_input:
std::cout << "Invalid input" << std::endl;
break;
case parse_error::overflow:
std::cout << "Overflow error" << std::endl;
break;
}
}
return 0;
}
在这个例子中,parse_number
函数尝试解析一个字符串为double
类型的数字。如果解析成功,它返回一个包含解析结果的std::expected
对象;如果解析失败,它返回一个包含错误信息的std::expected
对象。在main
函数中,我们可以通过检查result
是否为真来判断操作是否成功,如果成功则获取结果,否则处理相应的错误。
三、std::optional的单子式操作
3.1 transform
transform
函数用于对std::optional
中的值应用一个函数,并返回一个新的std::optional
,其中包含应用函数后的结果。如果std::optional
没有值,则返回一个空的std::optional
。其函数原型如下:
// 如果*this有值, 则返回F的结果(F返回不一定是optional<U>类型), 否则返回空的optional.
template<class F> constexpr auto transform(F&& f) &;
template<class F> constexpr auto transform(F&& f) const&;
template<class F> constexpr auto transform(F&& f) &&;
template<class F> constexpr auto transform(F&& f) const&&;
示例代码如下:
代码语言:cpp代码运行次数:0运行复制#include <iostream>
#include <optional>
#include <string>
int main() {
std::optional<int> opt = 42;
// 使用 transform 将 int 转换为 std::string
std::optional<std::string> result = opt.transform([](int value) {
return std::to_string(value);
});
if (result) {
std::cout << "Transformed value: " << *result << std::endl;
} else {
std::cout << "No value" << std::endl;
}
return 0;
}
在这个例子中,我们有一个包含int
值的std::optional
对象opt
,通过transform
函数将其中的int
值转换为std::string
类型,并将结果存储在新的std::optional
对象result
中。最后,我们检查result
是否有值并输出相应的结果。
3.2 and_then
and_then
函数用于链式调用一系列可能返回std::optional
的操作。如果std::optional
有值,则将该值传递给提供的函数,并返回该函数的结果;如果std::optional
没有值,则直接返回一个空的std::optional
。其函数原型如下:
// 如果*this有值, 则返回F的结果(F必须返回optional<U>类型), 否则返回空的optional.
template<class F> constexpr auto and_then(F&& f) &;
template<class F> constexpr auto and_then(F&& f) const&;
template<class F> constexpr auto and_then(F&& f) &&;
template<class F> constexpr auto and_then(F&& f) const&&;
示例代码如下:
代码语言:cpp代码运行次数:0运行复制#include <iostream>
#include <optional>
#include <string>
std::optional<int> 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;
else
return std::nullopt;
}
int main() {
std::optional<std::string> str_opt = "123";
auto result = str_opt.and_then(to_int);
if (result) {
std::cout << "Converted integer: " << *result << std::endl;
} else {
std::cout << "Conversion failed" << std::endl;
}
return 0;
}
在这个例子中,我们有一个包含std::string
的std::optional
对象str_opt
,通过and_then
函数将其中的字符串转换为int
类型。to_int
函数尝试将字符串解析为整数,如果解析成功则返回一个包含整数的std::optional
对象,否则返回std::nullopt
。最后,我们检查result
是否有值并输出相应的结果。
3.3 or_else
or_else
函数用于在std::optional
没有值的情况下提供一个默认值或执行一个替代操作。如果std::optional
有值,则返回该值;如果没有值,则返回提供的函数的结果。其函数原型如下:
// 如果*this有值, 则返回*this, 否则返回F的结果.
template<class F> constexpr auto or_else(F&& f) &;
template<class F> constexpr auto or_else(F&& f) const&;
template<class F> constexpr auto or_else(F&& f) &&;
template<class F> constexpr auto or_else(F&& f) const&&;
示例代码如下:
代码语言:cpp代码运行次数:0运行复制#include <iostream>
#include <optional>
#include <string>
int main() {
using maybe_int = std::optional<int>;
auto valueless = [] {
std::cout << "Valueless: ";
return maybe_int{0};
};
maybe_int x;
std::cout << x.or_else(valueless).value() << '\n';
x = 42;
std::cout << "Has value: ";
std::cout << x.or_else(valueless).value() << '\n';
x.reset();
std::cout << x.or_else(valueless).value() << '\n';
return 0;
}
在这个例子中,我们定义了一个valueless
函数,它在std::optional
没有值时返回一个包含0
的std::optional
对象。然后我们创建了一个std::optional<int>
对象x
,并多次调用or_else
函数。当x
没有值时,or_else
函数会调用valueless
函数并返回其结果;当x
有值时,or_else
函数直接返回x
的值。
四、std::expected的单子式操作
4.1 transform
transform
函数用于对std::expected
中的值应用一个函数,并返回一个新的std::expected
,其中包含应用函数后的结果。如果std::expected
包含错误,则直接返回包含相同错误的std::expected
对象。示例代码如下:
#include <iostream>
#include <expected>
#include <string>
std::expected<int, std::string> increment(int value) {
return value + 1;
}
int main() {
std::expected<int, std::string> exp = 42;
auto result = exp.transform(increment);
if (result) {
std::cout << "Transformed value: " << *result << std::endl;
} else {
std::cout << "Error: " << result.error() << std::endl;
}
return 0;
}
在这个例子中,我们有一个包含int
值的std::expected
对象exp
,通过transform
函数将其中的int
值加1
,并将结果存储在新的std::expected
对象result
中。最后,我们检查result
是否包含值并输出相应的结果。
4.2 and_then
and_then
成员函数用于链式调用一系列可能返回std::expected
的操作。它在std::expected
对象持有值时被调用,允许无缝地进行操作链,而无需在每个步骤后手动进行错误检查。示例代码如下:
#include <iostream>
#include <expected>
std::expected<int, std::string> incrementIfPositive(int value) {
if (value > 0)
return value + 1;
return std::unexpected("Value must be positive");
}
std::expected<int, std::string> getInput(int x) {
if (x % 2 == 0)
return x;
return std::unexpected("Value not even!");
}
int main() {
auto input = getInput(-2);
auto result = input.and_then(incrementIfPositive);
if (result)
std::cout << *result << '\n';
else
std::cout << result.error() << '\n';
return 0;
}
在这个例子中,getInput
函数返回一个std::expected
对象,如果输入是偶数则包含该偶数,否则包含错误信息。incrementIfPositive
函数接受一个整数,如果该整数为正,则返回加1
后的结果,否则返回错误信息。通过and_then
函数,我们可以将这两个操作链起来,避免了手动的错误检查。
4.3 or_else
or_else
函数用于在std::expected
包含错误的情况下提供一个替代操作或默认值。如果std::expected
包含值,则返回该值;如果包含错误,则调用提供的函数并返回其结果。示例代码如下:
#include <iostream>
#include <expected>
#include <string>
std::expected<int, std::string> divide(int a, int b) {
if (b == 0) {
return std::unexpected("Division by zero");
}
return a / b;
}
std::expected<int, std::string> fallback() {
return 0;
}
int main() {
auto result = divide(10, 0).or_else(fallback);
if (result) {
std::cout << "Result: " << *result << std::endl;
} else {
std::cout << "Error: " << result.error() << std::endl;
}
return 0;
}
在这个例子中,divide
函数尝试进行除法运算,如果除数为0
则返回错误信息。fallback
函数返回一个包含0
的std::expected
对象。通过or_else
函数,当divide
函数返回错误时,会调用fallback
函数并返回其结果。
五、总结
C++23中为std::optional
和std::expected
引入的单子式操作(transform、or_else与and_then)为C++编程带来了更强大的功能和更简洁的代码风格。这些操作借鉴了函数式编程的思想,使得我们可以更方便地处理可选值和错误情况,避免了大量的手动错误检查和嵌套的if-else
语句,提高了代码的可读性和可维护性。在实际开发中,合理使用这些操作可以让我们的代码更加健壮和优雅。
C++23中std::optional和std::expected的单子式操作
一、引言
在C++编程中,错误处理和可选值的管理是非常重要的部分。C++17引入了std::optional
,它提供了一种新的标准方式来表达可能缺失的值。而C++23在此基础上,不仅对std::optional
进行了扩展,还引入了std::expected
,并且为它们都提供了受函数式编程启发的新接口,特别是单子式操作(transform、or_else与and_then),这些操作可以简化代码并提高代码的可读性和可维护性。
二、std::optional和std::expected的基本概念
2.1 std::optional
std::optional<T>
是一个类模板,它管理一个可选的容纳值,即这个值既可以存在也可以不存在。一种常见的使用情况是作为一个可能失败的函数的返回值。与其他手段,如std::pair<T, bool>
相比,std::optional
能更好地处理构造开销高昂的对象,并且更加可读,因为它显式表达了意图。例如:
#include <optional> // 引入std::optional
#include <iostream>
std::optional<double> 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;
}
在这个例子中,getValue
函数可能返回一个double
值,也可能返回std::nullopt
表示没有值。在main
函数中,我们使用has_value
成员函数检查返回值是否存在,如果存在则使用解引用运算符*
来获取值。
2.2 std::expected
std::expected<T, E>
是C++23标准库中的新成员,旨在提供一种类型安全的方式来表示可能成功或失败的操作结果。它将有效结果或错误封装在单个对象内,本质上表示类型为T
的预期值或者类型为E
的意外错误。与传统的错误处理技术(如异常或错误码)不同,std::expected
避免了异常带来的复杂性和开销,使得编写健壮软件时更加方便。例如:
#include <expected>
#include <iostream>
#include <string>
enum class parse_error {
invalid_input,
overflow
};
auto parse_number(std::string_view& str) -> std::expected<double, parse_error> {
// 解析逻辑
// 如果解析失败,返回相应的错误
return std::unexpected(parse_error::invalid_input);
// 如果解析成功,返回预期的值
return 3.14;
}
int main() {
std::string_view input = "3.14";
auto result = parse_number(input);
if (result) {
std::cout << "Parsed number: " << *result << std::endl;
} else {
switch (result.error()) {
case parse_error::invalid_input:
std::cout << "Invalid input" << std::endl;
break;
case parse_error::overflow:
std::cout << "Overflow error" << std::endl;
break;
}
}
return 0;
}
在这个例子中,parse_number
函数尝试解析一个字符串为double
类型的数字。如果解析成功,它返回一个包含解析结果的std::expected
对象;如果解析失败,它返回一个包含错误信息的std::expected
对象。在main
函数中,我们可以通过检查result
是否为真来判断操作是否成功,如果成功则获取结果,否则处理相应的错误。
三、std::optional的单子式操作
3.1 transform
transform
函数用于对std::optional
中的值应用一个函数,并返回一个新的std::optional
,其中包含应用函数后的结果。如果std::optional
没有值,则返回一个空的std::optional
。其函数原型如下:
// 如果*this有值, 则返回F的结果(F返回不一定是optional<U>类型), 否则返回空的optional.
template<class F> constexpr auto transform(F&& f) &;
template<class F> constexpr auto transform(F&& f) const&;
template<class F> constexpr auto transform(F&& f) &&;
template<class F> constexpr auto transform(F&& f) const&&;
示例代码如下:
代码语言:cpp代码运行次数:0运行复制#include <iostream>
#include <optional>
#include <string>
int main() {
std::optional<int> opt = 42;
// 使用 transform 将 int 转换为 std::string
std::optional<std::string> result = opt.transform([](int value) {
return std::to_string(value);
});
if (result) {
std::cout << "Transformed value: " << *result << std::endl;
} else {
std::cout << "No value" << std::endl;
}
return 0;
}
在这个例子中,我们有一个包含int
值的std::optional
对象opt
,通过transform
函数将其中的int
值转换为std::string
类型,并将结果存储在新的std::optional
对象result
中。最后,我们检查result
是否有值并输出相应的结果。
3.2 and_then
and_then
函数用于链式调用一系列可能返回std::optional
的操作。如果std::optional
有值,则将该值传递给提供的函数,并返回该函数的结果;如果std::optional
没有值,则直接返回一个空的std::optional
。其函数原型如下:
// 如果*this有值, 则返回F的结果(F必须返回optional<U>类型), 否则返回空的optional.
template<class F> constexpr auto and_then(F&& f) &;
template<class F> constexpr auto and_then(F&& f) const&;
template<class F> constexpr auto and_then(F&& f) &&;
template<class F> constexpr auto and_then(F&& f) const&&;
示例代码如下:
代码语言:cpp代码运行次数:0运行复制#include <iostream>
#include <optional>
#include <string>
std::optional<int> 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;
else
return std::nullopt;
}
int main() {
std::optional<std::string> str_opt = "123";
auto result = str_opt.and_then(to_int);
if (result) {
std::cout << "Converted integer: " << *result << std::endl;
} else {
std::cout << "Conversion failed" << std::endl;
}
return 0;
}
在这个例子中,我们有一个包含std::string
的std::optional
对象str_opt
,通过and_then
函数将其中的字符串转换为int
类型。to_int
函数尝试将字符串解析为整数,如果解析成功则返回一个包含整数的std::optional
对象,否则返回std::nullopt
。最后,我们检查result
是否有值并输出相应的结果。
3.3 or_else
or_else
函数用于在std::optional
没有值的情况下提供一个默认值或执行一个替代操作。如果std::optional
有值,则返回该值;如果没有值,则返回提供的函数的结果。其函数原型如下:
// 如果*this有值, 则返回*this, 否则返回F的结果.
template<class F> constexpr auto or_else(F&& f) &;
template<class F> constexpr auto or_else(F&& f) const&;
template<class F> constexpr auto or_else(F&& f) &&;
template<class F> constexpr auto or_else(F&& f) const&&;
示例代码如下:
代码语言:cpp代码运行次数:0运行复制#include <iostream>
#include <optional>
#include <string>
int main() {
using maybe_int = std::optional<int>;
auto valueless = [] {
std::cout << "Valueless: ";
return maybe_int{0};
};
maybe_int x;
std::cout << x.or_else(valueless).value() << '\n';
x = 42;
std::cout << "Has value: ";
std::cout << x.or_else(valueless).value() << '\n';
x.reset();
std::cout << x.or_else(valueless).value() << '\n';
return 0;
}
在这个例子中,我们定义了一个valueless
函数,它在std::optional
没有值时返回一个包含0
的std::optional
对象。然后我们创建了一个std::optional<int>
对象x
,并多次调用or_else
函数。当x
没有值时,or_else
函数会调用valueless
函数并返回其结果;当x
有值时,or_else
函数直接返回x
的值。
四、std::expected的单子式操作
4.1 transform
transform
函数用于对std::expected
中的值应用一个函数,并返回一个新的std::expected
,其中包含应用函数后的结果。如果std::expected
包含错误,则直接返回包含相同错误的std::expected
对象。示例代码如下:
#include <iostream>
#include <expected>
#include <string>
std::expected<int, std::string> increment(int value) {
return value + 1;
}
int main() {
std::expected<int, std::string> exp = 42;
auto result = exp.transform(increment);
if (result) {
std::cout << "Transformed value: " << *result << std::endl;
} else {
std::cout << "Error: " << result.error() << std::endl;
}
return 0;
}
在这个例子中,我们有一个包含int
值的std::expected
对象exp
,通过transform
函数将其中的int
值加1
,并将结果存储在新的std::expected
对象result
中。最后,我们检查result
是否包含值并输出相应的结果。
4.2 and_then
and_then
成员函数用于链式调用一系列可能返回std::expected
的操作。它在std::expected
对象持有值时被调用,允许无缝地进行操作链,而无需在每个步骤后手动进行错误检查。示例代码如下:
#include <iostream>
#include <expected>
std::expected<int, std::string> incrementIfPositive(int value) {
if (value > 0)
return value + 1;
return std::unexpected("Value must be positive");
}
std::expected<int, std::string> getInput(int x) {
if (x % 2 == 0)
return x;
return std::unexpected("Value not even!");
}
int main() {
auto input = getInput(-2);
auto result = input.and_then(incrementIfPositive);
if (result)
std::cout << *result << '\n';
else
std::cout << result.error() << '\n';
return 0;
}
在这个例子中,getInput
函数返回一个std::expected
对象,如果输入是偶数则包含该偶数,否则包含错误信息。incrementIfPositive
函数接受一个整数,如果该整数为正,则返回加1
后的结果,否则返回错误信息。通过and_then
函数,我们可以将这两个操作链起来,避免了手动的错误检查。
4.3 or_else
or_else
函数用于在std::expected
包含错误的情况下提供一个替代操作或默认值。如果std::expected
包含值,则返回该值;如果包含错误,则调用提供的函数并返回其结果。示例代码如下:
#include <iostream>
#include <expected>
#include <string>
std::expected<int, std::string> divide(int a, int b) {
if (b == 0) {
return std::unexpected("Division by zero");
}
return a / b;
}
std::expected<int, std::string> fallback() {
return 0;
}
int main() {
auto result = divide(10, 0).or_else(fallback);
if (result) {
std::cout << "Result: " << *result << std::endl;
} else {
std::cout << "Error: " << result.error() << std::endl;
}
return 0;
}
在这个例子中,divide
函数尝试进行除法运算,如果除数为0
则返回错误信息。fallback
函数返回一个包含0
的std::expected
对象。通过or_else
函数,当divide
函数返回错误时,会调用fallback
函数并返回其结果。
五、总结
C++23中为std::optional
和std::expected
引入的单子式操作(transform、or_else与and_then)为C++编程带来了更强大的功能和更简洁的代码风格。这些操作借鉴了函数式编程的思想,使得我们可以更方便地处理可选值和错误情况,避免了大量的手动错误检查和嵌套的if-else
语句,提高了代码的可读性和可维护性。在实际开发中,合理使用这些操作可以让我们的代码更加健壮和优雅。
本文标签: C23中stdoptional和stdexpected的单子式操作
版权声明:本文标题:C++23中std::optional和std::expected的单子式操作 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/jiaocheng/1747411744a2165083.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论