diff --git a/CMakeLists.txt b/CMakeLists.txt
index c774f1e..a41b519 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,6 +10,7 @@ find_package(rcutils REQUIRED)
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 11)
endif()
+# TODO (ahcorde): Remove tl_expected when CXX 20 standard is available
# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
@@ -50,14 +51,40 @@ ament_export_dependencies(rcutils)
if(BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
- find_package(ament_lint_auto REQUIRED)
+
+ find_package(ament_cmake_copyright REQUIRED)
+ find_package(ament_cmake_cppcheck REQUIRED)
+ find_package(ament_cmake_cpplint REQUIRED)
+ find_package(ament_cmake_flake8 REQUIRED)
+ find_package(ament_cmake_lint_cmake REQUIRED)
+ find_package(ament_cmake_pep257 REQUIRED)
+ find_package(ament_cmake_uncrustify REQUIRED)
+ find_package(ament_cmake_xmllint REQUIRED)
+
+ set(
+ _linter_excludes
+ include/rcpputils/tl_expected/expected.hpp
+ )
+
+ ament_copyright(EXCLUDE ${_linter_excludes})
+ ament_cppcheck(
+ EXCLUDE ${_linter_excludes}
+ LANGUAGE c++
+ )
+ ament_cpplint(EXCLUDE ${_linter_excludes})
+ ament_flake8(EXCLUDE ${_linter_excludes})
+ ament_lint_cmake()
+ ament_pep257(EXCLUDE ${_linter_excludes})
+ ament_uncrustify(
+ EXCLUDE ${_linter_excludes}
+ LANGUAGE c++
+ )
+ ament_xmllint()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wthread-safety -Werror)
endif()
- ament_lint_auto_find_test_dependencies()
-
ament_add_gtest(test_asserts_ndebug test/test_asserts.cpp)
target_link_libraries(test_asserts_ndebug ${PROJECT_NAME})
diff --git a/include/rcpputils/tl_expected/expected.hpp b/include/rcpputils/tl_expected/expected.hpp
new file mode 100644
index 0000000..ae81698
--- /dev/null
+++ b/include/rcpputils/tl_expected/expected.hpp
@@ -0,0 +1,2326 @@
+///
+// expected - An implementation of std::expected with extensions
+// Written in 2017 by Simon Brand (simonrbrand@gmail.com, @TartanLlama)
+//
+// Documentation available at http://tl.tartanllama.xyz/
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// .
+///
+
+#ifndef TL_EXPECTED_HPP
+#define TL_EXPECTED_HPP
+
+#define TL_EXPECTED_VERSION_MAJOR 1
+#define TL_EXPECTED_VERSION_MINOR 0
+#define TL_EXPECTED_VERSION_PATCH 1
+
+#include
+#include
+#include
+#include
+
+#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+#define TL_EXPECTED_EXCEPTIONS_ENABLED
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+#define TL_EXPECTED_MSVC2015
+#define TL_EXPECTED_MSVC2015_CONSTEXPR
+#else
+#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC55
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+// GCC < 5 doesn't support overloading on const&& for member functions
+
+#define TL_EXPECTED_NO_CONSTRR
+// GCC < 5 doesn't support some standard C++11 type traits
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::has_trivial_copy_constructor
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::has_trivial_copy_assign
+
+// This one will be different for GCC 5.7 if it's ever supported
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible
+
+// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector
+// for non-copyable types
+#elif (defined(__GNUC__) && __GNUC__ < 8 && \
+ !defined(__clang__))
+#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace tl {
+ namespace detail {
+ template
+ struct is_trivially_copy_constructible : std::is_trivially_copy_constructible{};
+#ifdef _GLIBCXX_VECTOR
+ template
+ struct is_trivially_copy_constructible>
+ : std::false_type{};
+#endif
+ }
+}
+#endif
+
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ tl::detail::is_trivially_copy_constructible
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible
+#else
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::is_trivially_copy_constructible
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible
+#endif
+
+#if __cplusplus > 201103L
+#define TL_EXPECTED_CXX14
+#endif
+
+#ifdef TL_EXPECTED_GCC49
+#define TL_EXPECTED_GCC49_CONSTEXPR
+#else
+#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
+#endif
+
+#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
+ defined(TL_EXPECTED_GCC49))
+#define TL_EXPECTED_11_CONSTEXPR
+#else
+#define TL_EXPECTED_11_CONSTEXPR constexpr
+#endif
+
+namespace tl {
+template class expected;
+
+#ifndef TL_MONOSTATE_INPLACE_MUTEX
+#define TL_MONOSTATE_INPLACE_MUTEX
+class monostate {};
+
+struct in_place_t {
+ explicit in_place_t() = default;
+};
+static constexpr in_place_t in_place{};
+#endif
+
+template class unexpected {
+public:
+ static_assert(!std::is_same::value, "E must not be void");
+
+ unexpected() = delete;
+ constexpr explicit unexpected(const E &e) : m_val(e) {}
+
+ constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
+
+ constexpr const E &value() const & { return m_val; }
+ TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
+ TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
+ constexpr const E &&value() const && { return std::move(m_val); }
+
+private:
+ E m_val;
+};
+
+template
+constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) {
+ return lhs.value() == rhs.value();
+}
+template
+constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) {
+ return lhs.value() != rhs.value();
+}
+template
+constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) {
+ return lhs.value() < rhs.value();
+}
+template
+constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) {
+ return lhs.value() <= rhs.value();
+}
+template
+constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) {
+ return lhs.value() > rhs.value();
+}
+template
+constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) {
+ return lhs.value() >= rhs.value();
+}
+
+template
+unexpected::type> make_unexpected(E &&e) {
+ return unexpected::type>(std::forward(e));
+}
+
+struct unexpect_t {
+ unexpect_t() = default;
+};
+static constexpr unexpect_t unexpect{};
+
+namespace detail {
+template
+[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ throw std::forward(e);
+#else
+ #ifdef _MSC_VER
+ __assume(0);
+ #else
+ __builtin_unreachable();
+ #endif
+#endif
+}
+
+#ifndef TL_TRAITS_MUTEX
+#define TL_TRAITS_MUTEX
+// C++14-style aliases for brevity
+template using remove_const_t = typename std::remove_const::type;
+template
+using remove_reference_t = typename std::remove_reference::type;
+template using decay_t = typename std::decay::type;
+template
+using enable_if_t = typename std::enable_if::type;
+template
+using conditional_t = typename std::conditional::type;
+
+// std::conjunction from C++17
+template struct conjunction : std::true_type {};
+template struct conjunction : B {};
+template
+struct conjunction
+ : std::conditional, B>::type {};
+
+#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+#endif
+
+// In C++11 mode, there's an issue in libc++'s std::mem_fn
+// which results in a hard-error when using it in a noexcept expression
+// in some cases. This is a check to workaround the common failing case.
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+template struct is_pointer_to_non_const_member_func : std::false_type {};
+template
+struct is_pointer_to_non_const_member_func : std::true_type {};
+template
+struct is_pointer_to_non_const_member_func : std::true_type {};
+template
+struct is_pointer_to_non_const_member_func : std::true_type {};
+template
+struct is_pointer_to_non_const_member_func : std::true_type {};
+template
+struct is_pointer_to_non_const_member_func : std::true_type {};
+template
+struct is_pointer_to_non_const_member_func : std::true_type {};
+
+template struct is_const_or_const_ref : std::false_type {};
+template struct is_const_or_const_ref : std::true_type {};
+template struct is_const_or_const_ref : std::true_type {};
+#endif
+
+// std::invoke from C++17
+// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+template ::value
+ && is_const_or_const_ref::value)>,
+#endif
+ typename = enable_if_t>::value>,
+ int = 0>
+ constexpr auto invoke(Fn && f, Args && ... args) noexcept(
+ noexcept(std::mem_fn(f)(std::forward(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward(args)...)) {
+ return std::mem_fn(f)(std::forward(args)...);
+}
+
+template >::value>>
+ constexpr auto invoke(Fn && f, Args && ... args) noexcept(
+ noexcept(std::forward(f)(std::forward(args)...)))
+ -> decltype(std::forward(f)(std::forward(args)...)) {
+ return std::forward(f)(std::forward(args)...);
+}
+
+// std::invoke_result from C++17
+template struct invoke_result_impl;
+
+template
+struct invoke_result_impl<
+ F, decltype(detail::invoke(std::declval(), std::declval()...), void()),
+ Us...> {
+ using type = decltype(detail::invoke(std::declval(), std::declval()...));
+};
+
+template
+using invoke_result = invoke_result_impl;
+
+template
+using invoke_result_t = typename invoke_result::type;
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+// TODO make a version which works with MSVC 2015
+template struct is_swappable : std::true_type {};
+
+template struct is_nothrow_swappable : std::true_type {};
+#else
+// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+namespace swap_adl_tests {
+ // if swap ADL finds this then it would call std::swap otherwise (same
+ // signature)
+ struct tag {};
+
+ template tag swap(T&, T&);
+ template tag swap(T(&a)[N], T(&b)[N]);
+
+ // helper functions to test if an unqualified swap is possible, and if it
+ // becomes std::swap
+ template std::false_type can_swap(...) noexcept(false);
+ template (), std::declval()))>
+ std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(),
+ std::declval())));
+
+ template std::false_type uses_std(...);
+ template
+ std::is_same(), std::declval())), tag>
+ uses_std(int);
+
+ template
+ struct is_std_swap_noexcept
+ : std::integral_constant::value&&
+ std::is_nothrow_move_assignable::value> {};
+
+ template
+ struct is_std_swap_noexcept : is_std_swap_noexcept {};
+
+ template
+ struct is_adl_swap_noexcept
+ : std::integral_constant(0))> {};
+} // namespace swap_adl_tests
+
+template
+struct is_swappable
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap(0))::value &&
+ (!decltype(detail::swap_adl_tests::uses_std(0))::value ||
+ (std::is_move_assignable::value &&
+ std::is_move_constructible::value))> {};
+
+template
+struct is_swappable
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap(0))::value &&
+ (!decltype(
+ detail::swap_adl_tests::uses_std(0))::value ||
+ is_swappable::value)> {};
+
+template
+struct is_nothrow_swappable
+ : std::integral_constant<
+ bool,
+ is_swappable::value &&
+ ((decltype(detail::swap_adl_tests::uses_std(0))::value
+ && detail::swap_adl_tests::is_std_swap_noexcept::value) ||
+ (!decltype(detail::swap_adl_tests::uses_std(0))::value &&
+ detail::swap_adl_tests::is_adl_swap_noexcept::value))> {
+};
+#endif
+#endif
+
+// Trait for checking if a type is a tl::expected
+template struct is_expected_impl : std::false_type {};
+template
+struct is_expected_impl> : std::true_type {};
+template using is_expected = is_expected_impl>;
+
+template
+using expected_enable_forward_value = detail::enable_if_t<
+ std::is_constructible::value &&
+ !std::is_same, in_place_t>::value &&
+ !std::is_same, detail::decay_t>::value &&
+ !std::is_same, detail::decay_t>::value>;
+
+template
+using expected_enable_from_other = detail::enable_if_t<
+ std::is_constructible::value &&
+ std::is_constructible::value &&
+ !std::is_constructible &>::value &&
+ !std::is_constructible &&>::value &&
+ !std::is_constructible &>::value &&
+ !std::is_constructible &&>::value &&
+ !std::is_convertible &, T>::value &&
+ !std::is_convertible &&, T>::value &&
+ !std::is_convertible &, T>::value &&
+ !std::is_convertible &&, T>::value>;
+
+template
+using is_void_or = conditional_t::value, std::true_type, U>;
+
+template
+using is_copy_constructible_or_void =
+ is_void_or>;
+
+template
+using is_move_constructible_or_void =
+ is_void_or>;
+
+template
+using is_copy_assignable_or_void =
+ is_void_or>;
+
+
+template
+using is_move_assignable_or_void =
+ is_void_or>;
+
+
+} // namespace detail
+
+namespace detail {
+struct no_init_t {};
+static constexpr no_init_t no_init{};
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for where neither `T` or `E` is trivially
+// destructible, so the destructors must be called on destruction of the
+// `expected`
+template ::value,
+ bool = std::is_trivially_destructible::value>
+struct expected_storage_base {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template ::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&... args)
+ : m_val(std::forward(args)...), m_has_val(true) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list il,
+ Args &&... args)
+ : m_val(il, std::forward(args)...), m_has_val(true) {}
+ template ::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward(args)...), m_has_val(false) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list il,
+ Args &&... args)
+ : m_unexpect(il, std::forward(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (m_has_val) {
+ m_val.~T();
+ } else {
+ m_unexpect.~unexpected();
+ }
+ }
+ union {
+ T m_val;
+ unexpected m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// This specialization is for when both `T` and `E` are trivially-destructible,
+// so the destructor of the `expected` can be trivial.
+template struct expected_storage_base {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template ::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&... args)
+ : m_val(std::forward(args)...), m_has_val(true) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list il,
+ Args &&... args)
+ : m_val(il, std::forward(args)...), m_has_val(true) {}
+ template ::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward(args)...), m_has_val(false) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list il,
+ Args &&... args)
+ : m_unexpect(il, std::forward(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() = default;
+ union {
+ T m_val;
+ unexpected m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// T is trivial, E is not.
+template struct expected_storage_base {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
+ : m_no_init(), m_has_val(false) {}
+
+ template ::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&... args)
+ : m_val(std::forward(args)...), m_has_val(true) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list il,
+ Args &&... args)
+ : m_val(il, std::forward(args)...), m_has_val(true) {}
+ template ::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward(args)...), m_has_val(false) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list il,
+ Args &&... args)
+ : m_unexpect(il, std::forward(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (!m_has_val) {
+ m_unexpect.~unexpected();
+ }
+ }
+
+ union {
+ T m_val;
+ unexpected m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// E is trivial, T is not.
+template struct expected_storage_base {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template ::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&... args)
+ : m_val(std::forward(args)...), m_has_val(true) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list il,
+ Args &&... args)
+ : m_val(il, std::forward(args)...), m_has_val(true) {}
+ template ::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward(args)...), m_has_val(false) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list il,
+ Args &&... args)
+ : m_unexpect(il, std::forward(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (m_has_val) {
+ m_val.~T();
+ }
+ }
+ union {
+ T m_val;
+ unexpected m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template struct expected_storage_base {
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base() : m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
+
+ template ::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward(args)...), m_has_val(false) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list il,
+ Args &&... args)
+ : m_unexpect(il, std::forward(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() = default;
+ struct dummy {};
+ union {
+ unexpected m_unexpect;
+ dummy m_val;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template struct expected_storage_base {
+ constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
+
+ template ::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
+ : m_unexpect(std::forward(args)...), m_has_val(false) {}
+
+ template &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list il,
+ Args &&... args)
+ : m_unexpect(il, std::forward(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (!m_has_val) {
+ m_unexpect.~unexpected();
+ }
+ }
+
+ union {
+ unexpected m_unexpect;
+ char m_dummy;
+ };
+ bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template
+struct expected_operations_base : expected_storage_base {
+ using expected_storage_base::expected_storage_base;
+
+ template void construct(Args &&... args) noexcept {
+ new (std::addressof(this->m_val)) T(std::forward(args)...);
+ this->m_has_val = true;
+ }
+
+ template void construct_with(Rhs &&rhs) noexcept {
+ new (std::addressof(this->m_val)) T(std::forward(rhs).get());
+ this->m_has_val = true;
+ }
+
+ template void construct_error(Args &&... args) noexcept {
+ new (std::addressof(this->m_unexpect))
+ unexpected(std::forward(args)...);
+ this->m_has_val = false;
+ }
+
+ #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+
+ // These assign overloads ensure that the most efficient assignment
+ // implementation is used while maintaining the strong exception guarantee.
+ // The problematic case is where rhs has a value, but *this does not.
+ //
+ // This overload handles the case where we can just copy-construct `T`
+ // directly into place without throwing.
+ template ::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected();
+ construct(rhs.get());
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // This overload handles the case where we can attempt to create a copy of
+ // `T`, then no-throw move it into place if the copy was successful.
+ template