I am wanting to confirm that I am correctly using std::launder(...) ensuring that I correctly understand its usage.
I am creating a Result<U,E> in C++ based off of Rust's implementation.
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;
};
In the code I use using type = typename std::aligned_storage<sizeof(E), alignof(E)>::type; as my storage type. It is my belief that I need to use std::launder(...) when I return the error type from the function like so:
[[nodiscard]] constexpr E& get_error() & noexcept {
assert_err(tag_);
return *std::launder(reinterpret_cast<E*>(&error_));
}
The reason that I believe I need to use std::launder(...) is because since the incoming error type may be a struct possibly with a const value then it appears that if I do not use std::launder(...) then on first initialization it will refer to the const member value and if I was to reuse this allocated storage it would always refer to the initial const member value.
I have a rudimentary understanding of std::launder so an explanation of what circumstances require its usage would be appreciated. I have looked at the cppreference for this function but still find it rather mystifying.
Note: the full impl can be found on github.
error_type can bestd::optional<E>. This will massively simplify your design.std::optional<E>would be simpler. I didn't do so as I wanted to try and implement something from "scratch" with minimal help from the standard library as possible. I certainly thought about usingstd::optional<E>, but thought this would be an interesting manner to implement it and profile the resulting implementation. Edit: In the end it did lead to this question and so I think from a learning perspective it achieved what I desired.