std::expected在最受尊敬的 stackoverflow 答案之一中,我找到了模板类用法
的示例: What are coroutines in C++20?
同时我在 cppreference.com 上找不到任何关于此类的提及。你能解释一下它是什么吗?
我什么时候应该使用std::expected异常?什么时候应该使用异常?以这个函数为例:
int parse_int(std::string_view str) {
if (str.empty()) {
throw std::invalid_argument("string must not be empty");
}
/* ... */
if (/* result too large */) {
throw std::out_of_range("value exceeds maximum for int");
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
我想在使用此函数时区分不同的错误,因此抛出不同类型的异常很有用。但是,我也可以这样做std::expected:
enum class parse_error {
empty_string,
invalid_format,
out_of_range
};
std::expected<int, parse_error> parse_int(std::string_view str) noexcept {
if (str.empty()) {
return std::unexpected(parse_error::empty_string);
}
/* ... */
if (/* result too large */) {
return std::unexpected(parse_error::out_of_range);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
是否有任何理由使用std::expected异常(性能、代码大小、编译速度、ABI),或者只是风格偏好?
std::optional在 C++23 中,采用了一元运算 in ,后来又采用了std::expected. 为什么像and_then、、、这样的一元操作transform没有or_else包含在主论文中?
我正在阅读 cpp 23 标准,我偶然发现了std::unexpected.
Expected.un.cons部分定义
template<class Err = E>
constexpr explicit unexpected(Err&& e);
Run Code Online (Sandbox Code Playgroud)
具有以下约束(除其他外)
is_same_v<remove_cvref_t<Err>, unexpected> is false;
Run Code Online (Sandbox Code Playgroud)
考虑到unexpected是一个类模板,这个表达式什么时候为真?
如果我正在编写一个返回对象的函数std::expected,并可能调用其他返回std::expected对象的函数,我发现自己编写这样的代码片段非常常见。
struct Foo { };
std::expected<Foo, std::string> f();
auto res = f();
if(!res) return std::unexpected { res.error() };
auto val = res.value();
// do something with val
Run Code Online (Sandbox Code Playgroud)
所以我编写了一个这样的宏,在成功时“返回”值,在失败时“返回”错误。
#define CHECK(expr)\
({\
auto res = expr;\
if(!res) return std::unexpected { res.error() };\
res.value();\
})
Run Code Online (Sandbox Code Playgroud)
然后我可以这样使用它:
Foo foo = CHECK(f());
Run Code Online (Sandbox Code Playgroud)
我假设内部作用域中变量的生命周期应该与赋值表达式一样长。它是否正确?有没有什么情况可能会出错?
这是关于嵌入式 C++ 的。假设我有一个 struct Timer_t。
创建一个新对象
makeTimer()我们不能在设备上使用异常,并且要初始化计时器,我们可能会收到错误。
这就是为什么
std::expected<Timer_t,Error_t>由于我们使用 C++,人们往往会失败(太频繁地叫我来这里)
构造函数在这里只能使用一半,工厂函数对我们来说就是这样。
对于析构函数来说它工作得很好。如果我们离开了这个范围,我们就会取消它的初始化。事情应该是这样的。
现在问题开始了:如果我们返回makeTimer()对象,我们就可以移动。
更准确地说,我们称之为move constructor!
因此我们有 2 个对象,我们将对象称为析构函数。
更准确地说:
makeTimer() -> Timer_t() -> std::move/Timer_t(Timer_t &&) -> ~Timer_t() -> program ends -> ~Timer_t();
Run Code Online (Sandbox Code Playgroud)
对于移动来说,这是预期的行为。因此,它符合标准,但很烦人。
在嵌入式环境中,我发现人们在扩展代码时会面临很大的失败风险。
我只想在最后调用一次析构函数。
Error_t makeTimer(Timer_t & uninitialized Type)(会扼杀 std::expected 背后的想法并使代码不那么“好”)std::shared_ptr(额外费用...)或简单的布尔值。有没有更好的更清洁的想法来解决这个问题?我不可能是唯一一个拥有它的人。