mirror of
https://github.com/ETLCPP/etl.git
synced 2026-07-30 16:26:17 +08:00
Merge branch 'development' into fix-vector-const-cast
This commit is contained in:
commit
2fd20d03a6
@ -86,6 +86,8 @@ namespace etl
|
||||
static void destroy(char*, size_t) {}
|
||||
static void copy(char*, const char*, size_t) {}
|
||||
static void move(char*, const char*, size_t) {}
|
||||
static void copy_assign(char*, const char*, size_t) {}
|
||||
static void move_assign(char*, const char*, size_t) {}
|
||||
};
|
||||
|
||||
// Recursive case.
|
||||
@ -128,6 +130,30 @@ namespace etl
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_assign(char* dst, const char* src, size_t type_id)
|
||||
{
|
||||
if (type_id == Index)
|
||||
{
|
||||
copy_assign_impl(dst, src, etl::integral_constant<bool, etl::is_copy_assignable<THead>::value>{});
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_operations<Index + 1, TRest...>::copy_assign(dst, src, type_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void move_assign(char* dst, const char* src, size_t type_id)
|
||||
{
|
||||
if (type_id == Index)
|
||||
{
|
||||
move_assign_impl(dst, src, etl::integral_constant<bool, etl::is_move_assignable<THead>::value>{});
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_operations<Index + 1, TRest...>::move_assign(dst, src, type_id);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static void copy_impl(char* dst, const char* src, etl::true_type)
|
||||
@ -143,6 +169,20 @@ namespace etl
|
||||
}
|
||||
|
||||
static void move_impl(char*, const char*, etl::false_type) {}
|
||||
|
||||
static void copy_assign_impl(char* dst, const char* src, etl::true_type)
|
||||
{
|
||||
*reinterpret_cast<THead*>(dst) = *reinterpret_cast<const THead*>(src);
|
||||
}
|
||||
|
||||
static void copy_assign_impl(char*, const char*, etl::false_type) {}
|
||||
|
||||
static void move_assign_impl(char* dst, const char* src, etl::true_type)
|
||||
{
|
||||
*reinterpret_cast<THead*>(dst) = etl::move(*reinterpret_cast<THead*>(const_cast<char*>(src)));
|
||||
}
|
||||
|
||||
static void move_assign_impl(char*, const char*, etl::false_type) {}
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
@ -431,6 +471,80 @@ namespace etl
|
||||
{
|
||||
}
|
||||
|
||||
variant_base(const variant_base& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value)
|
||||
: type_id(other.type_id)
|
||||
{
|
||||
if (other.type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::copy(data, other.data, other.type_id);
|
||||
}
|
||||
}
|
||||
|
||||
variant_base(variant_base&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value)
|
||||
: type_id(other.type_id)
|
||||
{
|
||||
if (other.type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::move(data, other.data, other.type_id);
|
||||
}
|
||||
}
|
||||
|
||||
variant_base& operator=(const variant_base& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value
|
||||
&& etl::conjunction<etl::is_nothrow_copy_assignable<TTypes>...>::value)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if ((type_id != variant_npos) && (type_id == other.type_id))
|
||||
{
|
||||
variant_operations<0, TTypes...>::copy_assign(data, other.data, type_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::destroy(data, type_id);
|
||||
type_id = variant_npos;
|
||||
}
|
||||
|
||||
if (other.type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::copy(data, other.data, other.type_id);
|
||||
type_id = other.type_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
variant_base& operator=(variant_base&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value
|
||||
&& etl::conjunction<etl::is_nothrow_move_assignable<TTypes>...>::value)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if ((type_id != variant_npos) && (type_id == other.type_id))
|
||||
{
|
||||
variant_operations<0, TTypes...>::move_assign(data, other.data, type_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::destroy(data, type_id);
|
||||
type_id = variant_npos;
|
||||
}
|
||||
|
||||
if (other.type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::move(data, other.data, other.type_id);
|
||||
type_id = other.type_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~variant_base()
|
||||
{
|
||||
if (type_id != variant_npos)
|
||||
@ -474,7 +588,259 @@ namespace etl
|
||||
{
|
||||
}
|
||||
|
||||
variant_base(const variant_base& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value)
|
||||
: data()
|
||||
, type_id(other.type_id)
|
||||
{
|
||||
copy_construct_from(other, is_all_trivially_copyable{});
|
||||
}
|
||||
|
||||
variant_base(variant_base&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value)
|
||||
: data()
|
||||
, type_id(other.type_id)
|
||||
{
|
||||
move_construct_from(other, is_all_trivially_copyable{});
|
||||
}
|
||||
|
||||
variant_base& operator=(const variant_base& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value
|
||||
&& etl::conjunction<etl::is_nothrow_copy_assignable<TTypes>...>::value)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
copy_assign_from(other, is_all_trivially_copyable{});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
variant_base& operator=(variant_base&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value
|
||||
&& etl::conjunction<etl::is_nothrow_move_assignable<TTypes>...>::value)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
move_assign_from(other, is_all_trivially_copyable{});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~variant_base() = default;
|
||||
|
||||
private:
|
||||
|
||||
// All alternatives trivially copyable => a raw-byte copy correctly
|
||||
// reproduces both copy and move construction.
|
||||
typedef etl::integral_constant<bool, etl::conjunction<etl::is_trivially_copyable<TTypes>...>::value> is_all_trivially_copyable;
|
||||
|
||||
//*******************************************
|
||||
// Trivially copyable fast path: raw-byte copy.
|
||||
//*******************************************
|
||||
void copy_construct_from(const variant_base& other, etl::true_type) noexcept
|
||||
{
|
||||
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
|
||||
}
|
||||
|
||||
void move_construct_from(const variant_base& other, etl::true_type) noexcept
|
||||
{
|
||||
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
|
||||
}
|
||||
|
||||
void copy_assign_from(const variant_base& other, etl::true_type) noexcept
|
||||
{
|
||||
type_id = other.type_id;
|
||||
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
|
||||
}
|
||||
|
||||
void move_assign_from(const variant_base& other, etl::true_type) noexcept
|
||||
{
|
||||
type_id = other.type_id;
|
||||
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
// Non-trivial path: dispatch to the active alternative's own
|
||||
// copy/move constructor. The active union member shares its address
|
||||
// with the union, so the raw-pointer dispatch places the new object
|
||||
// in the correct storage.
|
||||
//*******************************************
|
||||
void copy_construct_from(const variant_base& other, etl::false_type)
|
||||
{
|
||||
if (other.type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::copy(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
|
||||
}
|
||||
}
|
||||
|
||||
void move_construct_from(variant_base& other, etl::false_type)
|
||||
{
|
||||
if (other.type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::move(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
|
||||
}
|
||||
}
|
||||
|
||||
void copy_assign_from(const variant_base& other, etl::false_type)
|
||||
{
|
||||
if ((type_id != variant_npos) && (type_id == other.type_id))
|
||||
{
|
||||
variant_operations<0, TTypes...>::copy_assign(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), type_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::destroy(reinterpret_cast<char*>(&data), type_id);
|
||||
type_id = variant_npos;
|
||||
}
|
||||
|
||||
if (other.type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::copy(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
|
||||
type_id = other.type_id;
|
||||
}
|
||||
}
|
||||
|
||||
void move_assign_from(variant_base& other, etl::false_type)
|
||||
{
|
||||
if ((type_id != variant_npos) && (type_id == other.type_id))
|
||||
{
|
||||
variant_operations<0, TTypes...>::move_assign(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), type_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::destroy(reinterpret_cast<char*>(&data), type_id);
|
||||
type_id = variant_npos;
|
||||
}
|
||||
|
||||
if (other.type_id != variant_npos)
|
||||
{
|
||||
variant_operations<0, TTypes...>::move(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
|
||||
type_id = other.type_id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Trait: are all types copy constructible?
|
||||
//*******************************************
|
||||
template <typename... TTypes>
|
||||
struct are_all_copy_constructible : etl::conjunction<etl::is_copy_constructible<TTypes>...>
|
||||
{
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Trait: are all types move constructible?
|
||||
//*******************************************
|
||||
template <typename... TTypes>
|
||||
struct are_all_move_constructible : etl::conjunction<etl::is_move_constructible<TTypes>...>
|
||||
{
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Trait: are all types copy assignable?
|
||||
//*******************************************
|
||||
template <typename... TTypes>
|
||||
struct are_all_copy_assignable : etl::conjunction<etl::is_copy_assignable<TTypes>...>
|
||||
{
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Trait: are all types move assignable?
|
||||
//*******************************************
|
||||
template <typename... TTypes>
|
||||
struct are_all_move_assignable : etl::conjunction<etl::is_move_assignable<TTypes>...>
|
||||
{
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Flat base that conditionally deletes the copy and move CONSTRUCTORS.
|
||||
/// Every special member is declared directly (no intermediate bases) so
|
||||
/// that MSVC does not spuriously delete the defaulted move members when a
|
||||
/// sibling/base subobject has a deleted special member.
|
||||
//*******************************************
|
||||
template <bool IsCopyConstructible, bool IsMoveConstructible>
|
||||
struct variant_constructor_control
|
||||
{
|
||||
variant_constructor_control() = default;
|
||||
variant_constructor_control(const variant_constructor_control&) = default;
|
||||
variant_constructor_control(variant_constructor_control&&) = default;
|
||||
variant_constructor_control& operator=(const variant_constructor_control&) = default;
|
||||
variant_constructor_control& operator=(variant_constructor_control&&) = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variant_constructor_control<false, true>
|
||||
{
|
||||
variant_constructor_control() = default;
|
||||
variant_constructor_control(const variant_constructor_control&) = delete;
|
||||
variant_constructor_control(variant_constructor_control&&) = default;
|
||||
variant_constructor_control& operator=(const variant_constructor_control&) = default;
|
||||
variant_constructor_control& operator=(variant_constructor_control&&) = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variant_constructor_control<true, false>
|
||||
{
|
||||
variant_constructor_control() = default;
|
||||
variant_constructor_control(const variant_constructor_control&) = default;
|
||||
variant_constructor_control(variant_constructor_control&&) = delete;
|
||||
variant_constructor_control& operator=(const variant_constructor_control&) = default;
|
||||
variant_constructor_control& operator=(variant_constructor_control&&) = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variant_constructor_control<false, false>
|
||||
{
|
||||
variant_constructor_control() = default;
|
||||
variant_constructor_control(const variant_constructor_control&) = delete;
|
||||
variant_constructor_control(variant_constructor_control&&) = delete;
|
||||
variant_constructor_control& operator=(const variant_constructor_control&) = default;
|
||||
variant_constructor_control& operator=(variant_constructor_control&&) = default;
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Flat base that conditionally deletes the copy and move ASSIGNMENT
|
||||
/// operators. Every special member is declared directly (see the note on
|
||||
/// variant_constructor_control above).
|
||||
//*******************************************
|
||||
template <bool IsCopyAssignable, bool IsMoveAssignable>
|
||||
struct variant_assignment_control
|
||||
{
|
||||
variant_assignment_control() = default;
|
||||
variant_assignment_control(const variant_assignment_control&) = default;
|
||||
variant_assignment_control(variant_assignment_control&&) = default;
|
||||
variant_assignment_control& operator=(const variant_assignment_control&) = default;
|
||||
variant_assignment_control& operator=(variant_assignment_control&&) = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variant_assignment_control<false, true>
|
||||
{
|
||||
variant_assignment_control() = default;
|
||||
variant_assignment_control(const variant_assignment_control&) = default;
|
||||
variant_assignment_control(variant_assignment_control&&) = default;
|
||||
variant_assignment_control& operator=(const variant_assignment_control&) = delete;
|
||||
variant_assignment_control& operator=(variant_assignment_control&&) = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variant_assignment_control<true, false>
|
||||
{
|
||||
variant_assignment_control() = default;
|
||||
variant_assignment_control(const variant_assignment_control&) = default;
|
||||
variant_assignment_control(variant_assignment_control&&) = default;
|
||||
variant_assignment_control& operator=(const variant_assignment_control&) = default;
|
||||
variant_assignment_control& operator=(variant_assignment_control&&) = delete;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variant_assignment_control<false, false>
|
||||
{
|
||||
variant_assignment_control() = default;
|
||||
variant_assignment_control(const variant_assignment_control&) = default;
|
||||
variant_assignment_control(variant_assignment_control&&) = default;
|
||||
variant_assignment_control& operator=(const variant_assignment_control&) = delete;
|
||||
variant_assignment_control& operator=(variant_assignment_control&&) = delete;
|
||||
};
|
||||
} // namespace private_variant
|
||||
|
||||
@ -483,7 +849,13 @@ namespace etl
|
||||
///\ingroup variant
|
||||
//***************************************************************************
|
||||
template <typename... TTypes>
|
||||
class variant : private private_variant::variant_base<private_variant::are_all_trivially_destructible<TTypes...>::value, TTypes...>
|
||||
class variant
|
||||
: private private_variant::variant_base<private_variant::are_all_trivially_destructible<TTypes...>::value, TTypes...>
|
||||
, private private_variant::variant_constructor_control< private_variant::are_all_copy_constructible<TTypes...>::value,
|
||||
private_variant::are_all_move_constructible<TTypes...>::value>
|
||||
, private private_variant::variant_assignment_control<
|
||||
private_variant::are_all_copy_constructible<TTypes...>::value && private_variant::are_all_copy_assignable<TTypes...>::value,
|
||||
private_variant::are_all_move_constructible<TTypes...>::value && private_variant::are_all_move_assignable<TTypes...>::value>
|
||||
{
|
||||
using base_type = private_variant::variant_base<private_variant::are_all_trivially_destructible<TTypes...>::value, TTypes...>;
|
||||
|
||||
@ -671,31 +1043,13 @@ namespace etl
|
||||
/// Copy constructor.
|
||||
///\param other The other variant object to copy.
|
||||
//***************************************************************************
|
||||
#include "diagnostic_uninitialized_push.h"
|
||||
ETL_CONSTEXPR14 variant(const variant& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value)
|
||||
: base_type(other.type_id)
|
||||
{
|
||||
if (other.index() != variant_npos)
|
||||
{
|
||||
do_copy_from(other, etl::integral_constant<bool, Is_Trivially_Destructible_Suite>{});
|
||||
}
|
||||
}
|
||||
#include "diagnostic_pop.h"
|
||||
variant(const variant& other) = default;
|
||||
|
||||
//***************************************************************************
|
||||
/// Move constructor.
|
||||
///\param other The other variant object to copy.
|
||||
//***************************************************************************
|
||||
#include "diagnostic_uninitialized_push.h"
|
||||
ETL_CONSTEXPR14 variant(variant&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value)
|
||||
: base_type(other.type_id)
|
||||
{
|
||||
if (other.index() != variant_npos)
|
||||
{
|
||||
do_move_from(other, etl::integral_constant<bool, Is_Trivially_Destructible_Suite>{});
|
||||
}
|
||||
}
|
||||
#include "diagnostic_pop.h"
|
||||
variant(variant&& other) = default;
|
||||
|
||||
//***************************************************************************
|
||||
/// Destructor.
|
||||
@ -806,51 +1160,13 @@ namespace etl
|
||||
/// Assignment operator for variant type.
|
||||
///\param other The variant to assign.
|
||||
//***************************************************************************
|
||||
variant& operator=(const variant& other) ETL_NOEXCEPT_IF((etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value))
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if (other.index() == variant_npos)
|
||||
{
|
||||
type_id = variant_npos;
|
||||
}
|
||||
else
|
||||
{
|
||||
do_destroy();
|
||||
|
||||
do_copy_from(other, etl::integral_constant<bool, Is_Trivially_Destructible_Suite>{});
|
||||
|
||||
type_id = other.type_id;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
variant& operator=(const variant& other) = default;
|
||||
|
||||
//***************************************************************************
|
||||
/// Assignment operator for variant type.
|
||||
/// Move assignment operator for variant type.
|
||||
///\param other The variant to assign.
|
||||
//***************************************************************************
|
||||
variant& operator=(variant&& other) ETL_NOEXCEPT_IF((etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value))
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
if (other.index() == variant_npos)
|
||||
{
|
||||
type_id = variant_npos;
|
||||
}
|
||||
else
|
||||
{
|
||||
do_destroy();
|
||||
|
||||
do_move_from(other, etl::integral_constant<bool, Is_Trivially_Destructible_Suite>{});
|
||||
|
||||
type_id = other.type_id;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
variant& operator=(variant&& other) = default;
|
||||
|
||||
//***************************************************************************
|
||||
/// Checks whether the variant doesn't contain a valid value.
|
||||
@ -1149,36 +1465,6 @@ namespace etl
|
||||
::new (static_cast<char*>(data)) T(etl::forward<U>(value));
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Copy from another variant.
|
||||
//***************************************************************************
|
||||
void do_copy_from(const variant& other, etl::integral_constant<bool, true>)
|
||||
{
|
||||
// Trivially destructible: copy via memcpy to avoid issues with non-trivial copy assignment in union.
|
||||
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
|
||||
}
|
||||
|
||||
void do_copy_from(const variant& other, etl::integral_constant<bool, false>)
|
||||
{
|
||||
// Non-trivially destructible: use switch-based dispatch.
|
||||
private_variant::variant_operations<0, TTypes...>::copy(data, other.data, other.type_id);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Move from another variant.
|
||||
//***************************************************************************
|
||||
void do_move_from(variant& other, etl::integral_constant<bool, true>)
|
||||
{
|
||||
// Trivially destructible: copy via memcpy (trivial move == copy).
|
||||
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
|
||||
}
|
||||
|
||||
void do_move_from(variant& other, etl::integral_constant<bool, false>)
|
||||
{
|
||||
// Non-trivially destructible: use switch-based dispatch.
|
||||
private_variant::variant_operations<0, TTypes...>::move(data, other.data, other.type_id);
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11)
|
||||
//***************************************************************************
|
||||
/// Call the relevant visitor by attempting each one.
|
||||
|
||||
@ -3323,9 +3323,30 @@ namespace etl
|
||||
|
||||
//*********************************************
|
||||
// is_assignable
|
||||
#if !ETL_USING_BUILTIN_IS_ASSIGNABLE && ETL_USING_CPP11
|
||||
namespace private_type_traits
|
||||
{
|
||||
template <typename, typename T1, typename T2>
|
||||
struct is_assignable_ : etl::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct is_assignable_<etl::void_t<decltype(etl::declval<T1>() = etl::declval<T2>())>, T1, T2> : etl::true_type
|
||||
{
|
||||
};
|
||||
} // namespace private_type_traits
|
||||
#endif
|
||||
|
||||
template <typename T1, typename T2>
|
||||
#if ETL_USING_BUILTIN_IS_ASSIGNABLE
|
||||
struct is_assignable : public etl::bool_constant<__is_assignable(T1, T2)>
|
||||
#elif ETL_USING_CPP11
|
||||
// Builtin-free assignability detection. Used when the __is_assignable builtin
|
||||
// is unavailable (e.g. MSVC, where determine_builtin_support.h disables the
|
||||
// __has_builtin block). Mirrors the void_t-based is_constructible above so
|
||||
// that class types are correctly reported as (copy/move) assignable.
|
||||
struct is_assignable : public private_type_traits::is_assignable_<etl::void_t<>, T1, T2>
|
||||
#else
|
||||
struct is_assignable
|
||||
: public etl::bool_constant< (etl::is_arithmetic<T1>::value || etl::is_pointer<T1>::value)
|
||||
|
||||
@ -321,6 +321,26 @@ namespace
|
||||
bool copied_to;
|
||||
};
|
||||
|
||||
//*********************************************
|
||||
// Copy constructible, but copy assignment is deleted.
|
||||
struct CopyConstructibleNonCopyAssignable
|
||||
{
|
||||
CopyConstructibleNonCopyAssignable() = default;
|
||||
CopyConstructibleNonCopyAssignable(const CopyConstructibleNonCopyAssignable&) = default;
|
||||
CopyConstructibleNonCopyAssignable& operator=(const CopyConstructibleNonCopyAssignable&) = delete;
|
||||
};
|
||||
|
||||
//*********************************************
|
||||
// Move constructible, but move assignment is deleted.
|
||||
struct MoveConstructibleNonMoveAssignable
|
||||
{
|
||||
MoveConstructibleNonMoveAssignable() = default;
|
||||
MoveConstructibleNonMoveAssignable(MoveConstructibleNonMoveAssignable&&) = default;
|
||||
MoveConstructibleNonMoveAssignable& operator=(MoveConstructibleNonMoveAssignable&&) = delete;
|
||||
MoveConstructibleNonMoveAssignable(const MoveConstructibleNonMoveAssignable&) = delete;
|
||||
MoveConstructibleNonMoveAssignable& operator=(const MoveConstructibleNonMoveAssignable&) = delete;
|
||||
};
|
||||
|
||||
//*********************************************
|
||||
// Trivially destructible, but non-copyable and non-movable.
|
||||
// Exercises the trivially-destructible (variadic_union) emplace path.
|
||||
@ -420,6 +440,109 @@ namespace
|
||||
|
||||
int value;
|
||||
};
|
||||
|
||||
//*********************************************
|
||||
// Non-trivially destructible type whose copy/move ASSIGNMENT has a
|
||||
// distinguishable effect from copy/move CONSTRUCTION. Used to verify that
|
||||
// same-type variant assignment dispatches through the alternative's own
|
||||
// assignment operator (no destroy + reconstruct). Exercises the
|
||||
// uninitialized_buffer (non-trivial) path.
|
||||
struct AssignTracker
|
||||
{
|
||||
AssignTracker(int value_)
|
||||
: value(value_)
|
||||
, constructed(true)
|
||||
, copy_assigned(false)
|
||||
, move_assigned(false)
|
||||
{
|
||||
}
|
||||
|
||||
AssignTracker(const AssignTracker& other)
|
||||
: value(other.value)
|
||||
, constructed(true)
|
||||
, copy_assigned(false)
|
||||
, move_assigned(false)
|
||||
{
|
||||
}
|
||||
|
||||
AssignTracker(AssignTracker&& other)
|
||||
: value(other.value)
|
||||
, constructed(true)
|
||||
, copy_assigned(false)
|
||||
, move_assigned(false)
|
||||
{
|
||||
}
|
||||
|
||||
~AssignTracker() {}
|
||||
|
||||
AssignTracker& operator=(const AssignTracker& rhs)
|
||||
{
|
||||
value = rhs.value;
|
||||
copy_assigned = true;
|
||||
move_assigned = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AssignTracker& operator=(AssignTracker&& rhs)
|
||||
{
|
||||
value = rhs.value;
|
||||
copy_assigned = false;
|
||||
move_assigned = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int value;
|
||||
bool constructed;
|
||||
bool copy_assigned;
|
||||
bool move_assigned;
|
||||
};
|
||||
|
||||
//*********************************************
|
||||
// Trivially destructible variant of AssignTracker (no user destructor) that
|
||||
// exercises the trivially-destructible (variadic_union) path.
|
||||
struct TrivialAssignTracker
|
||||
{
|
||||
TrivialAssignTracker(int value_)
|
||||
: value(value_)
|
||||
, copy_assigned(false)
|
||||
, move_assigned(false)
|
||||
{
|
||||
}
|
||||
|
||||
TrivialAssignTracker(const TrivialAssignTracker& other)
|
||||
: value(other.value)
|
||||
, copy_assigned(false)
|
||||
, move_assigned(false)
|
||||
{
|
||||
}
|
||||
|
||||
TrivialAssignTracker(TrivialAssignTracker&& other)
|
||||
: value(other.value)
|
||||
, copy_assigned(false)
|
||||
, move_assigned(false)
|
||||
{
|
||||
}
|
||||
|
||||
TrivialAssignTracker& operator=(const TrivialAssignTracker& rhs)
|
||||
{
|
||||
value = rhs.value;
|
||||
copy_assigned = true;
|
||||
move_assigned = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TrivialAssignTracker& operator=(TrivialAssignTracker&& rhs)
|
||||
{
|
||||
value = rhs.value;
|
||||
copy_assigned = false;
|
||||
move_assigned = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int value;
|
||||
bool copy_assigned;
|
||||
bool move_assigned;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Moved from the top of the file otherwise clang has issues with
|
||||
@ -560,6 +683,26 @@ struct etl::is_move_constructible<NonTrivialNonMovable> : public etl::false_type
|
||||
template <>
|
||||
struct etl::is_move_constructible<NonAddressable> : public etl::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct etl::is_copy_constructible<AssignTracker> : public etl::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct etl::is_move_constructible<AssignTracker> : public etl::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct etl::is_copy_constructible<TrivialAssignTracker> : public etl::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct etl::is_move_constructible<TrivialAssignTracker> : public etl::true_type
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -999,6 +1142,146 @@ namespace
|
||||
CHECK_EQUAL(variant_1_etl.index(), variant_2_etl.index());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Issue #1512: a variant must only be copy constructible / copy assignable
|
||||
// when every alternative is copy constructible. Moveable is move-only
|
||||
// (its copy operations are deleted), so a variant containing it must not
|
||||
// be copyable, while remaining move constructible / move assignable.
|
||||
TEST(test_copy_disabled_with_non_copyable_alternative)
|
||||
{
|
||||
typedef etl::variant<int, Moveable> non_copyable_variant;
|
||||
|
||||
CHECK(!etl::is_copy_constructible<non_copyable_variant>::value);
|
||||
CHECK(!etl::is_copy_assignable<non_copyable_variant>::value);
|
||||
CHECK(etl::is_move_constructible<non_copyable_variant>::value);
|
||||
CHECK(etl::is_move_assignable<non_copyable_variant>::value);
|
||||
|
||||
// A variant of purely copyable alternatives must remain copyable.
|
||||
typedef etl::variant<int, Copyable> copyable_variant;
|
||||
|
||||
CHECK(etl::is_copy_constructible<copyable_variant>::value);
|
||||
CHECK(etl::is_copy_assignable<copyable_variant>::value);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// A non-copy-assignable alternative must disable the variant's copy
|
||||
// assignment operator, and a non-move-assignable alternative must disable
|
||||
// the variant's move assignment operator, while leaving the corresponding
|
||||
// constructors available.
|
||||
TEST(test_assignment_disabled_with_non_assignable_alternative)
|
||||
{
|
||||
typedef etl::variant<int, CopyConstructibleNonCopyAssignable> copy_non_assignable_variant;
|
||||
|
||||
CHECK(etl::is_copy_constructible<copy_non_assignable_variant>::value);
|
||||
CHECK(!etl::is_copy_assignable<copy_non_assignable_variant>::value);
|
||||
|
||||
typedef etl::variant<int, MoveConstructibleNonMoveAssignable> move_non_assignable_variant;
|
||||
|
||||
CHECK(etl::is_move_constructible<move_non_assignable_variant>::value);
|
||||
CHECK(!etl::is_move_assignable<move_non_assignable_variant>::value);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// When source and destination hold the SAME alternative, assignment must
|
||||
// dispatch through that alternative's own assignment operator rather than
|
||||
// destroying and reconstructing it. Non-trivially-destructible path.
|
||||
TEST(test_copy_assign_same_type_uses_alternative_assignment)
|
||||
{
|
||||
typedef etl::variant<int, AssignTracker> tracker_variant;
|
||||
|
||||
tracker_variant variant_1(etl::in_place_type_t<AssignTracker>{}, 1);
|
||||
tracker_variant variant_2(etl::in_place_type_t<AssignTracker>{}, 2);
|
||||
|
||||
variant_1 = variant_2;
|
||||
|
||||
CHECK_EQUAL(1U, variant_1.index());
|
||||
CHECK_EQUAL(2, etl::get<AssignTracker>(variant_1).value);
|
||||
CHECK(etl::get<AssignTracker>(variant_1).copy_assigned);
|
||||
CHECK(!etl::get<AssignTracker>(variant_1).move_assigned);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_move_assign_same_type_uses_alternative_assignment)
|
||||
{
|
||||
typedef etl::variant<int, AssignTracker> tracker_variant;
|
||||
|
||||
tracker_variant variant_1(etl::in_place_type_t<AssignTracker>{}, 1);
|
||||
tracker_variant variant_2(etl::in_place_type_t<AssignTracker>{}, 2);
|
||||
|
||||
variant_1 = etl::move(variant_2);
|
||||
|
||||
CHECK_EQUAL(1U, variant_1.index());
|
||||
CHECK_EQUAL(2, etl::get<AssignTracker>(variant_1).value);
|
||||
CHECK(!etl::get<AssignTracker>(variant_1).copy_assigned);
|
||||
CHECK(etl::get<AssignTracker>(variant_1).move_assigned);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// When source and destination hold DIFFERENT alternatives, assignment must
|
||||
// still destroy and reconstruct.
|
||||
TEST(test_copy_assign_different_type_reconstructs)
|
||||
{
|
||||
typedef etl::variant<int, AssignTracker> tracker_variant;
|
||||
|
||||
tracker_variant variant_1(etl::in_place_type_t<int>{}, 5);
|
||||
tracker_variant variant_2(etl::in_place_type_t<AssignTracker>{}, 2);
|
||||
|
||||
variant_1 = variant_2;
|
||||
|
||||
CHECK_EQUAL(1U, variant_1.index());
|
||||
CHECK_EQUAL(2, etl::get<AssignTracker>(variant_1).value);
|
||||
CHECK(etl::get<AssignTracker>(variant_1).constructed);
|
||||
CHECK(!etl::get<AssignTracker>(variant_1).copy_assigned);
|
||||
CHECK(!etl::get<AssignTracker>(variant_1).move_assigned);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Same as above but for the trivially-destructible (variadic_union) path.
|
||||
TEST(test_assign_same_type_uses_alternative_assignment_trivial)
|
||||
{
|
||||
typedef etl::variant<int, TrivialAssignTracker> tracker_variant;
|
||||
|
||||
tracker_variant variant_1(etl::in_place_type_t<TrivialAssignTracker>{}, 1);
|
||||
tracker_variant variant_2(etl::in_place_type_t<TrivialAssignTracker>{}, 2);
|
||||
|
||||
variant_1 = variant_2;
|
||||
|
||||
CHECK_EQUAL(1U, variant_1.index());
|
||||
CHECK_EQUAL(2, etl::get<TrivialAssignTracker>(variant_1).value);
|
||||
CHECK(etl::get<TrivialAssignTracker>(variant_1).copy_assigned);
|
||||
CHECK(!etl::get<TrivialAssignTracker>(variant_1).move_assigned);
|
||||
|
||||
tracker_variant variant_3(etl::in_place_type_t<TrivialAssignTracker>{}, 3);
|
||||
variant_1 = etl::move(variant_3);
|
||||
|
||||
CHECK_EQUAL(1U, variant_1.index());
|
||||
CHECK_EQUAL(3, etl::get<TrivialAssignTracker>(variant_1).value);
|
||||
CHECK(etl::get<TrivialAssignTracker>(variant_1).move_assigned);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Issue #1512: even though the copy operations are disabled at compile
|
||||
// time, a move-only variant must still move construct / move assign
|
||||
// correctly, preserving the active alternative.
|
||||
TEST(test_move_only_variant_moves_correctly)
|
||||
{
|
||||
typedef etl::variant<int, Moveable> non_copyable_variant;
|
||||
|
||||
non_copyable_variant variant_1(etl::in_place_type_t<Moveable>{});
|
||||
CHECK_EQUAL(1U, variant_1.index());
|
||||
|
||||
// Move construction preserves the active alternative.
|
||||
non_copyable_variant variant_2(etl::move(variant_1));
|
||||
CHECK_EQUAL(1U, variant_2.index());
|
||||
CHECK(etl::get<Moveable>(variant_2).moved_to);
|
||||
|
||||
// Move assignment preserves the active alternative.
|
||||
non_copyable_variant variant_3;
|
||||
variant_3 = etl::move(variant_2);
|
||||
CHECK_EQUAL(1U, variant_3.index());
|
||||
CHECK(etl::get<Moveable>(variant_3).moved_to);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_assign_from_value)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user