Fix etl::optional for deleted copy constructors (#1362)

* Print test names at test time (#1343)

* Fix etl::optional for deleted copy constructors

---------

Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
This commit is contained in:
Roland Reichwein 2026-03-25 09:14:33 +01:00 committed by GitHub
parent 0b5621e809
commit bd15e42440
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 104 additions and 89 deletions

View File

@ -169,21 +169,19 @@ namespace etl
}
//***************************************************************************
/// Constructor from value type.
/// Converting constructor from value type.
/// Constructs T in-place from U&&, without requiring T to be
/// copy/move constructible.
//***************************************************************************
template <typename U,
typename etl::enable_if<
etl::is_constructible<T, U&&>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::in_place_t>::value &&
!etl::is_same<typename etl::decay<U>::type, optional_impl>::value, int>::type = 0>
ETL_CONSTEXPR20_STL
optional_impl(const T& value_)
optional_impl(U&& value_)
{
storage.construct(value_);
}
//***************************************************************************
/// Constructor from value type.
//***************************************************************************
ETL_CONSTEXPR20_STL
optional_impl(T&& value_)
{
storage.construct(etl::move(value_));
storage.construct(etl::forward<U>(value_));
}
//***************************************************************************
@ -280,23 +278,24 @@ namespace etl
//***************************************************************************
/// Assignment operator from value type.
//***************************************************************************
#if ETL_USING_CPP11
template <typename U,
typename etl::enable_if<
etl::is_constructible<T, U&&>::value &&
!etl::is_same<typename etl::decay<U>::type, optional_impl>::value, int>::type = 0>
ETL_CONSTEXPR20_STL
optional_impl& operator =(U&& value_)
{
storage.construct(etl::forward<U>(value_));
return *this;
}
#else
ETL_CONSTEXPR20_STL
optional_impl& operator =(const T& value_)
{
storage.construct(value_);
return *this;
}
#if ETL_USING_CPP11
//***************************************************************************
/// Assignment operator from value type.
//***************************************************************************
ETL_CONSTEXPR20_STL
optional_impl& operator =(T&& value_)
{
storage.construct(etl::move(value_));
return *this;
}
#endif
@ -1442,22 +1441,36 @@ namespace etl
#if ETL_USING_CPP11
//***************************************************************************
/// Construct from value type.
/// Converting constructor from value type.
/// Constructs T in-place from U&&, without requiring T to be
/// copy/move constructible.
//***************************************************************************
template <typename U = T, ETL_OPTIONAL_ENABLE_CPP14>
template <typename U,
typename etl::enable_if<
etl::is_constructible<T, U&&>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::optional<T>>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::in_place_t>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::nullopt_t>::value &&
etl::is_pod<typename etl::remove_cv<T>::type>::value, int>::type = 0>
ETL_CONSTEXPR14
optional(const T& value_)
: impl_t(value_)
optional(U&& value_)
: impl_t(etl::forward<U>(value_))
{
}
//***************************************************************************
/// Construct from value type.
/// Converting constructor from value type.
//***************************************************************************
template <typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL>
template <typename U,
typename etl::enable_if<
etl::is_constructible<T, U&&>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::optional<T>>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::in_place_t>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::nullopt_t>::value &&
!etl::is_pod<typename etl::remove_cv<T>::type>::value, int>::type = 0>
ETL_CONSTEXPR20_STL
optional(const T& value_)
: impl_t(value_)
optional(U&& value_)
: impl_t(etl::forward<U>(value_))
{
}
#else
@ -1470,29 +1483,6 @@ namespace etl
}
#endif
#if ETL_USING_CPP11
//***************************************************************************
/// Move construct from value type.
//***************************************************************************
template <typename U = T, ETL_OPTIONAL_ENABLE_CPP14>
ETL_CONSTEXPR14
optional(T&& value_)
: impl_t(etl::move(value_))
{
}
//***************************************************************************
/// Move construct from value type.
//***************************************************************************
template <typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL>
ETL_CONSTEXPR20_STL
optional(T&& value_)
: impl_t(etl::move(value_))
{
}
#endif
#if ETL_USING_CPP11
//***************************************************************************
/// Emplace construct from arguments.
@ -1641,25 +1631,35 @@ namespace etl
#if ETL_USING_CPP11
//***************************************************************************
/// Assignment operator from value type.
/// Converting assignment operator from value type.
//***************************************************************************
template <typename U = T, ETL_OPTIONAL_ENABLE_CPP14>
template <typename U,
typename etl::enable_if<
etl::is_constructible<T, U&&>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::optional<T>>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::nullopt_t>::value &&
etl::is_pod<typename etl::remove_cv<T>::type>::value, int>::type = 0>
ETL_CONSTEXPR14
optional& operator =(const T& value_)
optional& operator =(U&& value_)
{
impl_t::operator=(value_);
impl_t::operator=(etl::forward<U>(value_));
return *this;
}
//***************************************************************************
/// Assignment operator from value type.
/// Converting assignment operator from value type.
//***************************************************************************
template <typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL>
template <typename U,
typename etl::enable_if<
etl::is_constructible<T, U&&>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::optional<T>>::value &&
!etl::is_same<typename etl::decay<U>::type, etl::nullopt_t>::value &&
!etl::is_pod<typename etl::remove_cv<T>::type>::value, int>::type = 0>
ETL_CONSTEXPR20_STL
optional& operator =(const T& value_)
optional& operator =(U&& value_)
{
impl_t::operator=(value_);
impl_t::operator=(etl::forward<U>(value_));
return *this;
}
@ -1675,32 +1675,6 @@ namespace etl
}
#endif
#if ETL_USING_CPP11
//***************************************************************************
/// Move assignment operator from value type.
//***************************************************************************
template <typename U = T, ETL_OPTIONAL_ENABLE_CPP14>
ETL_CONSTEXPR14
optional& operator =(T&& value_)
{
impl_t::operator=(etl::move(value_));
return *this;
}
//***************************************************************************
/// Move assignment operator from value type.
//***************************************************************************
template <typename U = T, ETL_OPTIONAL_ENABLE_CPP20_STL>
ETL_CONSTEXPR20_STL
optional& operator =(T&& value_)
{
impl_t::operator=(etl::move(value_));
return *this;
}
#endif
//***************************************************************************
/// Returns an iterator to the beginning of the optional.
//***************************************************************************

View File

@ -1118,5 +1118,46 @@ namespace
CHECK_EQUAL(42, *opt);
}
//*************************************************************************
// GitHub issue #146: etl::optional doesn't compile with deleted copy constructor
//*************************************************************************
#if ETL_USING_CPP11
struct Issue146_NonCopyable
{
Issue146_NonCopyable(int some) : _some(some) {}
Issue146_NonCopyable(const Issue146_NonCopyable&) = delete;
Issue146_NonCopyable(Issue146_NonCopyable&&) = delete;
Issue146_NonCopyable& operator=(const Issue146_NonCopyable&) = delete;
int _some;
};
struct Issue146_Container
{
Issue146_Container(int a_val) : a(a_val) {}
Issue146_Container() : a(etl::nullopt) {}
etl::optional<Issue146_NonCopyable> a;
};
TEST(test_optional_issue_146_deleted_copy_ctor)
{
// etl::optional<T> should compile when T has deleted copy/move constructors,
// as long as T is constructible from the given arguments.
Issue146_Container with_value(42);
Issue146_Container without_value;
CHECK_TRUE(with_value.a.has_value());
CHECK_EQUAL(42, with_value.a->_some);
CHECK_FALSE(without_value.a.has_value());
// in_place construction should also work
etl::optional<Issue146_NonCopyable> opt(etl::in_place_t{}, 99);
CHECK_TRUE(opt.has_value());
CHECK_EQUAL(99, opt->_some);
}
#endif
}
}