我希望确认我是否正确使用std::launder(...)
,以确保我正确理解其用法。
我正在创建一个基于 Rust 实现的 C++ Result<U,E>
。
template <typename E>
class ResultStorage<void, E, std::enable_if_t<std::is_trivially_destructible_v<E>>> {
using type = typename std::aligned_storage<sizeof(E), alignof(E)>::type;
public:
explicit constexpr ResultStorage(const Ok<void>&) noexcept : tag_(ResultTag::OK) {}
explicit constexpr ResultStorage(const Ok<void>&&) noexcept : tag_(ResultTag::OK) {}
explicit constexpr ResultStorage(const Err<E>& err) noexcept(std::is_nothrow_copy_constructible<E>())
: tag_(ResultTag::ERR) {
new (&error_) E(err.get_error());
}
explicit constexpr ResultStorage(const Err<E>&& err) noexcept(std::is_nothrow_move_constructible<E>())
: tag_(ResultTag::ERR) {
new (&error_) E(std::move(err.get_error()));
}
~ResultStorage() = default;
[[nodiscard]] constexpr E& get_error() & noexcept {
assert_err(tag_);
return *std::launder(reinterpret_cast<E*>(&error_));
}
// Code omitted for brevity
private:
ResultTag tag_;
type error_;
template <typename Rv, typename Ev>
friend class result::Result;
};
在我的代码中,我使用
using type = typename std::aligned_storage<sizeof(E), alignof(E)>::type;
作为我的存储类型。 我认为当我从函数中返回错误类型时,需要使用std::launder(...)
,如下所示: [[nodiscard]] constexpr E& get_error() & noexcept {
assert_err(tag_);
return *std::launder(reinterpret_cast<E*>(&error_));
}
我相信我需要使用
std::launder(...)
是因为传入的错误类型可能是一个结构体,可能具有 const
值,如果不使用 std::launder(...)
,则在第一次初始化时它将引用 const
成员值。如果我要重复使用该分配的存储,它将始终引用最初的 const
成员值。我对
std::launder
有一个初步的了解,所以需要解释在什么情况下需要使用它。我已经查看了cppreference中有关此函数的信息,但仍然觉得难以理解。注意:完整的实现可以在 GitHub 上找到。
error_
类型可以是std::optional<E>
。这将极大地简化你的设计。 - bolovstd::optional<E>
会更简单。但我没有这样做,因为我想尽可能少地依赖标准库,尝试从零开始实现一些东西。我当然考虑过使用std::optional<E>
,但我认为这将是一个有趣的方式来实现它并对结果进行分析。编辑:最终它确实引发了这个问题,所以我认为从学习的角度来看,它达到了我想要的效果。 - cogle