Implement sane variant converting constructor (P0608R3)
* include/std/variant (__overload_set): Remove. (_Arr): New helper. (_Build_FUN): New class template to define a single FUN overload, with specializations to prevent unwanted conversions, as per P0608R3. (_Build_FUNs): New class template to build an overload set of FUN. (_FUN_type): New alias template to perform overload resolution. (__accepted_type): Use integer_constant base for failure case. Use _FUN_type for successful case. (variant::__accepted_index): Use _Tp instead of _Tp&&. (variant::variant(_Tp&&)): Likewise. (variant::operator=(_Tp&&)): Likewise. From-SVN: r271296
This commit is contained in:
parent
b62dcd16aa
commit
d069df01ed
@ -1,5 +1,17 @@
|
|||||||
2019-05-16 Jonathan Wakely <jwakely@redhat.com>
|
2019-05-16 Jonathan Wakely <jwakely@redhat.com>
|
||||||
|
|
||||||
|
* include/std/variant (__overload_set): Remove.
|
||||||
|
(_Arr): New helper.
|
||||||
|
(_Build_FUN): New class template to define a single FUN overload,
|
||||||
|
with specializations to prevent unwanted conversions, as per P0608R3.
|
||||||
|
(_Build_FUNs): New class template to build an overload set of FUN.
|
||||||
|
(_FUN_type): New alias template to perform overload resolution.
|
||||||
|
(__accepted_type): Use integer_constant base for failure case. Use
|
||||||
|
_FUN_type for successful case.
|
||||||
|
(variant::__accepted_index): Use _Tp instead of _Tp&&.
|
||||||
|
(variant::variant(_Tp&&)): Likewise.
|
||||||
|
(variant::operator=(_Tp&&)): Likewise.
|
||||||
|
|
||||||
* include/std/variant (_Variant_storage<false, _Types...>::_M_reset):
|
* include/std/variant (_Variant_storage<false, _Types...>::_M_reset):
|
||||||
Replace raw visitation with a runtime check for the valueless state
|
Replace raw visitation with a runtime check for the valueless state
|
||||||
and a non-raw visitor.
|
and a non-raw visitor.
|
||||||
|
@ -161,7 +161,7 @@ namespace __detail
|
|||||||
{
|
{
|
||||||
namespace __variant
|
namespace __variant
|
||||||
{
|
{
|
||||||
// Returns the first apparence of _Tp in _Types.
|
// Returns the first appearence of _Tp in _Types.
|
||||||
// Returns sizeof...(_Types) if _Tp is not in _Types.
|
// Returns sizeof...(_Types) if _Tp is not in _Types.
|
||||||
template<typename _Tp, typename... _Types>
|
template<typename _Tp, typename... _Types>
|
||||||
struct __index_of : std::integral_constant<size_t, 0> {};
|
struct __index_of : std::integral_constant<size_t, 0> {};
|
||||||
@ -727,41 +727,65 @@ namespace __variant
|
|||||||
inline constexpr bool __exactly_once =
|
inline constexpr bool __exactly_once =
|
||||||
__tuple_count_v<_Tp, tuple<_Types...>> == 1;
|
__tuple_count_v<_Tp, tuple<_Types...>> == 1;
|
||||||
|
|
||||||
// Takes _Types and create an overloaded _S_fun for each type.
|
// Helper used to check for valid conversions that don't involve narrowing.
|
||||||
// If a type appears more than once in _Types, create only one overload.
|
template<typename _Ti> struct _Arr { _Ti _M_x[1]; };
|
||||||
template<typename... _Types>
|
|
||||||
struct __overload_set
|
|
||||||
{ static void _S_fun(); };
|
|
||||||
|
|
||||||
template<typename _First, typename... _Rest>
|
// Build an imaginary function FUN(Ti) for each alternative type Ti
|
||||||
struct __overload_set<_First, _Rest...> : __overload_set<_Rest...>
|
template<size_t _Ind, typename _Tp, typename _Ti,
|
||||||
|
bool _Ti_is_cv_bool = is_same_v<remove_cv_t<_Ti>, bool>,
|
||||||
|
typename = void>
|
||||||
|
struct _Build_FUN
|
||||||
{
|
{
|
||||||
using __overload_set<_Rest...>::_S_fun;
|
// This function means 'using _Build_FUN<I, T, Ti>::_S_fun;' is valid,
|
||||||
static integral_constant<size_t, sizeof...(_Rest)> _S_fun(_First);
|
// but only static functions will be considered in the call below.
|
||||||
|
void _S_fun();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... _Rest>
|
// ... for which Ti x[] = {std::forward<T>(t)}; is well-formed,
|
||||||
struct __overload_set<void, _Rest...> : __overload_set<_Rest...>
|
template<size_t _Ind, typename _Tp, typename _Ti>
|
||||||
|
struct _Build_FUN<_Ind, _Tp, _Ti, false,
|
||||||
|
void_t<decltype(_Arr<_Ti>{{std::declval<_Tp>()}})>>
|
||||||
{
|
{
|
||||||
using __overload_set<_Rest...>::_S_fun;
|
// This is the FUN function for type _Ti, with index _Ind
|
||||||
|
static integral_constant<size_t, _Ind> _S_fun(_Ti);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper for variant(_Tp&&) and variant::operator=(_Tp&&).
|
// ... and if Ti is cv bool, remove_cvref_t<T> is bool.
|
||||||
// __accepted_index maps an arbitrary _Tp to an alternative type in _Variant
|
template<size_t _Ind, typename _Tp, typename _Ti>
|
||||||
// (or to variant_npos).
|
struct _Build_FUN<_Ind, _Tp, _Ti, true,
|
||||||
|
enable_if_t<is_same_v<__remove_cvref_t<_Tp>, bool>>>
|
||||||
|
{
|
||||||
|
// This is the FUN function for when _Ti is cv bool, with index _Ind
|
||||||
|
static integral_constant<size_t, _Ind> _S_fun(_Ti);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename _Tp, typename _Variant,
|
||||||
|
typename = make_index_sequence<variant_size_v<_Variant>>>
|
||||||
|
struct _Build_FUNs;
|
||||||
|
|
||||||
|
template<typename _Tp, typename... _Ti, size_t... _Ind>
|
||||||
|
struct _Build_FUNs<_Tp, variant<_Ti...>, index_sequence<_Ind...>>
|
||||||
|
: _Build_FUN<_Ind, _Tp, _Ti>...
|
||||||
|
{
|
||||||
|
using _Build_FUN<_Ind, _Tp, _Ti>::_S_fun...;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The index j of the overload FUN(Tj) selected by overload resolution
|
||||||
|
// for FUN(std::forward<_Tp>(t))
|
||||||
|
template<typename _Tp, typename _Variant>
|
||||||
|
using _FUN_type
|
||||||
|
= decltype(_Build_FUNs<_Tp, _Variant>::_S_fun(std::declval<_Tp>()));
|
||||||
|
|
||||||
|
// The index selected for FUN(std::forward<T>(t)), or variant_npos if none.
|
||||||
template<typename _Tp, typename _Variant, typename = void>
|
template<typename _Tp, typename _Variant, typename = void>
|
||||||
struct __accepted_index
|
struct __accepted_index
|
||||||
{ static constexpr size_t value = variant_npos; };
|
: integral_constant<size_t, variant_npos>
|
||||||
|
{ };
|
||||||
|
|
||||||
template<typename _Tp, typename... _Types>
|
template<typename _Tp, typename _Variant>
|
||||||
struct __accepted_index<
|
struct __accepted_index<_Tp, _Variant, void_t<_FUN_type<_Tp, _Variant>>>
|
||||||
_Tp, variant<_Types...>,
|
: _FUN_type<_Tp, _Variant>
|
||||||
void_t<decltype(__overload_set<_Types...>::_S_fun(std::declval<_Tp>()))>>
|
{ };
|
||||||
{
|
|
||||||
static constexpr size_t value = sizeof...(_Types) - 1
|
|
||||||
- decltype(__overload_set<_Types...>::
|
|
||||||
_S_fun(std::declval<_Tp>()))::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns the raw storage for __v.
|
// Returns the raw storage for __v.
|
||||||
template<typename _Variant>
|
template<typename _Variant>
|
||||||
@ -1247,8 +1271,8 @@ namespace __variant
|
|||||||
__exactly_once = __detail::__variant::__exactly_once<_Tp, _Types...>;
|
__exactly_once = __detail::__variant::__exactly_once<_Tp, _Types...>;
|
||||||
|
|
||||||
template<typename _Tp>
|
template<typename _Tp>
|
||||||
static constexpr size_t __accepted_index =
|
static constexpr size_t __accepted_index
|
||||||
__detail::__variant::__accepted_index<_Tp&&, variant>::value;
|
= __detail::__variant::__accepted_index<_Tp, variant>::value;
|
||||||
|
|
||||||
template<size_t _Np, typename = enable_if_t<(_Np < sizeof...(_Types))>>
|
template<size_t _Np, typename = enable_if_t<(_Np < sizeof...(_Types))>>
|
||||||
using __to_type = variant_alternative_t<_Np, variant>;
|
using __to_type = variant_alternative_t<_Np, variant>;
|
||||||
@ -1290,7 +1314,7 @@ namespace __variant
|
|||||||
constexpr
|
constexpr
|
||||||
variant(_Tp&& __t)
|
variant(_Tp&& __t)
|
||||||
noexcept(is_nothrow_constructible_v<_Tj, _Tp>)
|
noexcept(is_nothrow_constructible_v<_Tj, _Tp>)
|
||||||
: variant(in_place_index<__accepted_index<_Tp&&>>,
|
: variant(in_place_index<__accepted_index<_Tp>>,
|
||||||
std::forward<_Tp>(__t))
|
std::forward<_Tp>(__t))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
@ -1344,7 +1368,7 @@ namespace __variant
|
|||||||
noexcept(is_nothrow_assignable_v<__accepted_type<_Tp&&>&, _Tp>
|
noexcept(is_nothrow_assignable_v<__accepted_type<_Tp&&>&, _Tp>
|
||||||
&& is_nothrow_constructible_v<__accepted_type<_Tp&&>, _Tp>)
|
&& is_nothrow_constructible_v<__accepted_type<_Tp&&>, _Tp>)
|
||||||
{
|
{
|
||||||
constexpr auto __index = __accepted_index<_Tp&&>;
|
constexpr auto __index = __accepted_index<_Tp>;
|
||||||
if (index() == __index)
|
if (index() == __index)
|
||||||
std::get<__index>(*this) = std::forward<_Tp>(__rhs);
|
std::get<__index>(*this) = std::forward<_Tp>(__rhs);
|
||||||
else
|
else
|
||||||
|
@ -142,6 +142,11 @@ void arbitrary_ctor()
|
|||||||
static_assert(noexcept(variant<int, DefaultNoexcept>(int{})));
|
static_assert(noexcept(variant<int, DefaultNoexcept>(int{})));
|
||||||
static_assert(!noexcept(variant<int, Empty>(Empty{})));
|
static_assert(!noexcept(variant<int, Empty>(Empty{})));
|
||||||
static_assert(noexcept(variant<int, DefaultNoexcept>(DefaultNoexcept{})));
|
static_assert(noexcept(variant<int, DefaultNoexcept>(DefaultNoexcept{})));
|
||||||
|
|
||||||
|
// P0608R3 disallow narrowing conversions and boolean conversions
|
||||||
|
static_assert(!is_constructible_v<variant<int>, long>);
|
||||||
|
static_assert(!is_constructible_v<variant<bool>, int>);
|
||||||
|
static_assert(!is_constructible_v<variant<bool>, void*>);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct none { none() = delete; };
|
struct none { none() = delete; };
|
||||||
|
@ -102,6 +102,32 @@ void arbitrary_ctor()
|
|||||||
variant<int, string> v("a");
|
variant<int, string> v("a");
|
||||||
VERIFY(holds_alternative<string>(v));
|
VERIFY(holds_alternative<string>(v));
|
||||||
VERIFY(get<1>(v) == "a");
|
VERIFY(get<1>(v) == "a");
|
||||||
|
|
||||||
|
{
|
||||||
|
// P0608R3
|
||||||
|
variant<string, bool> x = "abc";
|
||||||
|
VERIFY(x.index() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// P0608R3
|
||||||
|
struct U {
|
||||||
|
U(char16_t c) : c(c) { }
|
||||||
|
char16_t c;
|
||||||
|
};
|
||||||
|
variant<char, U> x = u'\u2043';
|
||||||
|
VERIFY(x.index() == 1);
|
||||||
|
VERIFY(std::get<1>(x).c == u'\u2043');
|
||||||
|
|
||||||
|
struct Double {
|
||||||
|
Double(double& d) : d(d) { }
|
||||||
|
double& d;
|
||||||
|
};
|
||||||
|
double d = 3.14;
|
||||||
|
variant<int, Double> y = d;
|
||||||
|
VERIFY(y.index() == 1);
|
||||||
|
VERIFY(std::get<1>(y).d == d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ThrowingMoveCtorThrowsCopyCtor
|
struct ThrowingMoveCtorThrowsCopyCtor
|
||||||
@ -168,6 +194,27 @@ void arbitrary_assign()
|
|||||||
|
|
||||||
VERIFY(holds_alternative<string>(variant<int, string>("a")));
|
VERIFY(holds_alternative<string>(variant<int, string>("a")));
|
||||||
VERIFY(get<1>(v) == "a");
|
VERIFY(get<1>(v) == "a");
|
||||||
|
|
||||||
|
{
|
||||||
|
// P0608R3
|
||||||
|
using T1 = variant<float, int>;
|
||||||
|
T1 v1;
|
||||||
|
v1 = 0;
|
||||||
|
VERIFY(v1.index() == 1);
|
||||||
|
|
||||||
|
using T2 = variant<float, long>;
|
||||||
|
T2 v2;
|
||||||
|
v2 = 0;
|
||||||
|
VERIFY(v2.index() == 1);
|
||||||
|
|
||||||
|
struct big_int {
|
||||||
|
big_int(int) { }
|
||||||
|
};
|
||||||
|
using T3 = variant<float, big_int>;
|
||||||
|
T3 v3;
|
||||||
|
v3 = 0;
|
||||||
|
VERIFY(v3.index() == 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dtor()
|
void dtor()
|
||||||
|
Loading…
Reference in New Issue
Block a user