提问人:Sickeroni 提问时间:6/12/2023 最后编辑:marc_sSickeroni 更新时间:7/10/2023 访问量:143
在使用具有 std::expected 返回的工厂函数时,如何避免两次调用析构函数,而无需运行时成本
How to avoid calling the destructor twice when using a factory function with a std::expected return without runtime costs
问:
这是关于嵌入式 C++。假设我有一个结构体。Timer_t
创建新对象
- 构造函数是私有的
- 我们有一个工厂功能作为公共成员
makeTimer()
我们不能在设备上使用异常,为了初始化计时器,我们可能会遇到错误。
这就是为什么
- 默认构造函数对用户是隐藏的
- 使用工厂函数
- 工厂函数返回
std::expected<Timer_t,Error_t>
由于我们使用 C++,人们往往会失败(经常在这里叫我)
- 构造函数的初始化功能很强大
- 析构函数在反初始化方面功能强大
构造函数在这里只有一半可用,工厂函数就是这个。
对于析构函数,它工作得很好。如果我们离开范围,我们会取消初始化它。这就是它应该的样子。
现在问题开始了:如果我们在 Object 中返回,我们就会移动。makeTimer()
更准确地说,我们称之为 !move constructor
因此,我们有 2 个对象,对于该对象,我们称之为析构函数。
更准确地说:
makeTimer() -> Timer_t() -> std::move/Timer_t(Timer_t &&) -> ~Timer_t() -> program ends -> ~Timer_t();
对于移动,这是预期的行为。因此,它符合标准,但很烦人。
在嵌入式的上下文中,我看到人们在扩展代码时会在这里失败的风险很大。
我只想在最后调用析构函数一次。
- 如果我使用Timer_t作为回报,它就可以了!(这就是令人沮丧的地方)
- 我禁止使用非平凡的类型(呃!或者我从来没有看到过这个特定的计时器的好例子)
- use(会扼杀std::expected背后的想法,并使代码不那么“好”)
Error_t makeTimer(Timer_t & uninitialized Type)
- 使用标志/计数器,例如(额外费用...)或简单的布尔值。
std::shared_ptr
有没有更好更清洁的想法来解决这个问题?我不能是唯一一个拥有它的人。
答:
7赞
Jan Schultke
6/12/2023
#1
多亏了 RVO(返回值优化),您描述的问题在 C++ 中并不存在。请考虑以下代码:
#include <expected>
struct Timer_t {
Timer_t();
Timer_t(Timer_t&&);
Timer_t& operator=(Timer_t&&);
~Timer_t();
};
struct Error_t {
Error_t();
Error_t(Error_t&&);
Error_t& operator=(Error_t&&);
~Error_t();
};
std::expected<Timer_t, Error_t> makeTimer() {
return {};
}
int main() {
auto timer = makeTimer();
}
如果没有 RVO,看起来首先必须创建一个默认构造的 ,它将调用 ,然后调用移动构造函数,因为右侧是 prvalue。return {}
std::expected
Timer_t()
auto timer = makeTimer();
但是,自 C++17 以来,它的工作方式就不是这样了:
auto timer = makeTimer()
受复制省略的影响,因此不会调用任何移动构造函数。- 在 中,由于 是与返回类型类型相同的 prValue,因此它受 RVO 的约束。
return {};
{}
这两项优化都是强制性的,因此可以保证我们只调用一次,然后在超出范围时调用。正如预期的那样,这是编译器的输出:Timer_t()
~Timer_t()
timer
main:
sub rsp, 24
lea rdi, [rsp+14]
call Timer_t::Timer_t() [complete object constructor]
lea rdi, [rsp+14]
mov BYTE PTR [rsp+15], 1
call Timer_t::~Timer_t() [complete object destructor]
xor eax, eax
add rsp, 24
ret
请参阅使用 GCC 13 和 -std=c++2b -O2
的实时示例。
评论
0赞
Sickeroni
6/12/2023
谢谢!快速,非常精确的完美答案!
评论