std::optional 在C++23中新增了一些支持链式调用的函数:
三个函数最终都返回optiona,只是F函数的返回值可能不一样。
transform(F): 如果*this有值, 则返回F的结果(F返回不一定是optional<U>类型,其他不是optional都可以), 否则返回空的optional.
and_then(F): 如果*this有值, 则返回F的结果(F必须返回optional<U>类型), 否则返回空的optional.
or_else(F): 如果*this有值, 则返回*this, 否则返回F的结果(F必须返回optional<U>类型).
transform
:对值进行转换,保持optional
结构。and_then
:链式调用可能失败的操作,自动处理空值。or_else
:为空时提供备用值或恢复逻辑。
std::optional<T>::transform
If *this contains a value, invokes f with the contained value as an argument, and returns an std::optional that contains the result of that invocation; otherwise, returns an empty std::optional.
- 功能:若
optional
有值,则应用给定函数到该值,返回包装结果的optional
;否则返回空optional
。 - F的返回值不一定是optional的。
#include <optional>
#include <iostream>
int foo(int x)
{
return x*2;
}
int main()
{
std::optional<int> o1 = 42;
auto doubled = o1.transform(foo); // 返回 std::optional<int> 包含 84
std::optional<int> o2;
auto empty = o2.transform([](int n) { return n * 2; }); // 返回空 optional
std::cout << doubled.value_or(0) << ", " << empty.value_or(0) << std::endl; // 输出: 84, 0
}
链式di
#include <iostream>
#include <optional>
struct 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::optional<A> o_A)
{
std::cout << (o_A ? "o_A has a value\n" : "o_A is empty\n");
std::optional<D> 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( {} );
}
Output:
o_A has a value
A => B
B => C
C => D
o_D has a value
o_A is empty
o_D is empty
std::optional<T>::and_then
If *this contains a value, invokes f with the contained value as an argument, and returns the result of that invocation; otherwise, returns an empty std::optional.
The return type (see below) must be a specialization of std::optional (unlike transform()). Otherwise, the program is ill-formed.
- 功能:若
optional
有值,则应用返回optional
的函数,并直接返回该结果(展平嵌套);否则返回空。 - f返回值必须是optional<T>的
#include <optional>
#include <string>
std::optional<std::string> int_to_str(int n)
{
if (n >= 0) return std::to_string(n);
else return std::nullopt;
}
int main()
{
std::optional<int> o1 = 10;
auto str1 = o1.and_then(int_to_str); // 返回 std::optional<std::string> 包含 "10"
std::optional<int> o2 = -5;
auto str2 = o2.and_then(int_to_str); // 返回空 optional
std::optional<int> o3;
auto str3 = o3.and_then(int_to_str); // 返回空 optional
}
std::optional<T>::or_else
Returns *this if it contains a value. Otherwise, returns the result of f.
- 功能:若
optional
为空,则调用给定函数生成替代的optional
;否则返回原值。 - F返回值必须是optional<T>的
#include <iostream>
#include <optional>
#include <string>
int main()
{
using maybe_int = std::optional<int>;
auto valuesless = []
{
std::cout << "Valueless: ";
return maybe_int{0};
};
maybe_int x;
std::cout << x.or_else(valuesless).value() << std::endl;
x = 42;
std::cout << x.or_else(valuesless).value() << std::endl;
x.reset();
std::cout << x.or_else(valuesless).value() << std::endl;
}
Output:
Valueless: 0
Has value: 42
Valueless: 0
综合示例:
#include <optional>
#include <iostream>
#include <string>
std::optional<std::string> get_input(bool valid)
{
return valid ? std::optional{"42"} : std::nullopt;
}
std::optional<int> parse(const std::string& s)
{
try
{
return std::stoi(s);
}
catch (...)
{
return std::nullopt;
}
}
int main()
{
// 有效输入
auto result1 = get_input(true)
.and_then(parse) // 返回 optional<int> 42
.transform([](int n) { return n * 2; }) // 返回 optional<int> 84
.or_else([] { return std::optional<int>(0); }); // 仍为 84
// 无效输入
auto result2 = get_input(false)
.and_then(parse) // 返回空
.transform([](int n) { return n * 2; }) // 仍为空
.or_else([] { return std::optional<int>(0); }); // 返回 0
std::cout << result1.value() << ", " << result2.value() << std::endl; // 输出: 84, 0
}
### 不同点
1. **返回类型**:
- `transform`:应用的函数返回一个值,`transform` 返回一个包含该值的 `std::optional`。
- `and_then`:应用的函数返回一个 `std::optional`,`and_then` 返回这个新的 `std::optional`。
2. **适用场景**:
- `transform`:适用于需要对 `std::optional` 中的值进行转换,并且转换后的结果不需要是 `std::optional` 的场景。
- `and_then`:适用于需要对 `std::optional` 中的值进行转换,并且转换后的结果也是 `std::optional` 的场景。
这两个函数提供了更灵活和简洁的方式来处理 `std::optional` 中的值。
Eg1:
#include <iostream>
#include <optional>
#include <string>
#include <vector>
using namespace std;
optional<int> Parse(const string& s)
{
try
{
return stoi(s);
}
catch (...)
{
return {};
}
}
int main()
{
vector<string> inputs{"12", "nan"};
for (auto& s : inputs)
{
std::cout << s << std::endl;
auto result =
#if 1
Parse(s)
.and_then([](int value) -> optional<int> { std::cout << "call and_then, "; return value * 2; })
.transform([](int value) { std::cout << "call transform, "; return to_string(value); })
.or_else([] { return optional<string>{"No Integer Found"}; });
#else
Parse(s)
.and_then([](int value) -> optional<string> { std::cout << "call and_then, "; return to_string(value * 2); }) //不能没有optional,跟transform的区别;
.transform([](std::string value) { std::cout << "call transform, "; return value; })
.or_else([] { return optional<string>{"No Integer Found"}; });
#endif
cout << *result << endl << std::endl;
}
}
Eg2:
#include <charconv>
#include <iomanip>
#include <iostream>
#include <optional>
#include <ranges>
#include <string>
#include <string_view>
#include <vector>
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()
{
using namespace std::literals;
const std::vector<std::optional<std::string>> v
{
"1234", "15 foo", "bar", "42", "5000000000", " 5", std::nullopt, "-43"
};
for (auto&& x : v | std::views::transform(
[](auto&& o)
{
// debug print the content of input optional<string>
std::cout << std::left << std::setw(13)
<< std::quoted(o.value_or("nullopt")) << " -> ";
return o
// if optional is nullopt convert it to optional with "" string
.or_else([]{ return std::optional<std::string>{""}; })
// flatmap from strings to ints (making empty optionals where it fails)
.and_then(to_int)
// map int to int + 1
.transform([](int n) { return n + 1; })
// convert back to strings
.transform([](int n) { return std::to_string(n); })
// replace all empty optionals that were left by
// and_then and ignored by transforms with "NaN"
.value_or("NaN"s);
}))
{
std::cout << x << '\n';
}
}