forked from OSchip/llvm-project
[libc++] [C++2b] [P2162] Allow inheritance from std::variant.
This patch changes the variant even in pre-C++2b. It should not break anything, only allow use cases that didn't work previously. Notes: `__as_variant` is used in `__visitation::__variant::__visit_alt`, but I haven't used it in `__visitation::__variant::__visit_alt_at`. That's because it is used only in `__visit_value_at`, which in turn is always used on variant specializations (that's in comparison operators). * https://wg21.link/P2162 Reviewed By: ldionne, #libc, Quuxplusone Differential Revision: https://reviews.llvm.org/D97394
This commit is contained in:
parent
7f2236cf58
commit
0324b46cd8
|
@ -7,7 +7,7 @@
|
|||
"`P1682R3 <https://wg21.link/P1682R3>`__","LWG","std::to_underlying for enumerations","February 2021","|Complete|","13.0"
|
||||
"`P2017R1 <https://wg21.link/P2017R1>`__","LWG","Conditionally borrowed ranges","February 2021","",""
|
||||
"`P2160R1 <https://wg21.link/P2160R1>`__","LWG","Locks lock lockables","February 2021","",""
|
||||
"`P2162R2 <https://wg21.link/P2162R2>`__","LWG","Inheriting from std::variant","February 2021","",""
|
||||
"`P2162R2 <https://wg21.link/P2162R2>`__","LWG","Inheriting from std::variant","February 2021","|Complete|","13.0"
|
||||
"`P2212R2 <https://wg21.link/P2212R2>`__","LWG","Relax Requirements for time_point::clock","February 2021","",""
|
||||
"`P2259R1 <https://wg21.link/P2259R1>`__","LWG","Repairing input range adaptors and counted_iterator","February 2021","",""
|
||||
"","","","","",""
|
||||
|
|
|
|
@ -162,7 +162,7 @@ Status
|
|||
------------------------------------------------- -----------------
|
||||
``__cpp_lib_unordered_map_try_emplace`` ``201411L``
|
||||
------------------------------------------------- -----------------
|
||||
``__cpp_lib_variant`` ``201606L``
|
||||
``__cpp_lib_variant`` ``202102L``
|
||||
------------------------------------------------- -----------------
|
||||
``__cpp_lib_void_t`` ``201411L``
|
||||
------------------------------------------------- -----------------
|
||||
|
|
|
@ -318,6 +318,33 @@ using __variant_index_t =
|
|||
template <class _IndexType>
|
||||
constexpr _IndexType __variant_npos = static_cast<_IndexType>(-1);
|
||||
|
||||
template <class... _Types>
|
||||
class _LIBCPP_TEMPLATE_VIS variant;
|
||||
|
||||
template <class... _Types>
|
||||
_LIBCPP_INLINE_VISIBILITY constexpr variant<_Types...>&
|
||||
__as_variant(variant<_Types...>& __vs) noexcept {
|
||||
return __vs;
|
||||
}
|
||||
|
||||
template <class... _Types>
|
||||
_LIBCPP_INLINE_VISIBILITY constexpr const variant<_Types...>&
|
||||
__as_variant(const variant<_Types...>& __vs) noexcept {
|
||||
return __vs;
|
||||
}
|
||||
|
||||
template <class... _Types>
|
||||
_LIBCPP_INLINE_VISIBILITY constexpr variant<_Types...>&&
|
||||
__as_variant(variant<_Types...>&& __vs) noexcept {
|
||||
return _VSTD::move(__vs);
|
||||
}
|
||||
|
||||
template <class... _Types>
|
||||
_LIBCPP_INLINE_VISIBILITY constexpr const variant<_Types...>&&
|
||||
__as_variant(const variant<_Types...>&& __vs) noexcept {
|
||||
return _VSTD::move(__vs);
|
||||
}
|
||||
|
||||
namespace __find_detail {
|
||||
|
||||
template <class _Tp, class... _Types>
|
||||
|
@ -564,8 +591,9 @@ struct __variant {
|
|||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
static constexpr decltype(auto) __visit_alt(_Visitor&& __visitor,
|
||||
_Vs&&... __vs) {
|
||||
return __base::__visit_alt(_VSTD::forward<_Visitor>(__visitor),
|
||||
_VSTD::forward<_Vs>(__vs).__impl...);
|
||||
return __base::__visit_alt(
|
||||
_VSTD::forward<_Visitor>(__visitor),
|
||||
_VSTD::__as_variant(_VSTD::forward<_Vs>(__vs)).__impl...);
|
||||
}
|
||||
|
||||
template <class _Visitor, class... _Vs>
|
||||
|
@ -586,6 +614,7 @@ struct __variant {
|
|||
__make_value_visitor(_VSTD::forward<_Visitor>(__visitor)),
|
||||
_VSTD::forward<_Vs>(__vs)...);
|
||||
}
|
||||
|
||||
#if _LIBCPP_STD_VER > 17
|
||||
template <class _Rp, class _Visitor, class... _Vs>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
|
@ -1637,18 +1666,21 @@ constexpr bool operator>=(const variant<_Types...>& __lhs,
|
|||
|
||||
template <class... _Vs>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
|
||||
constexpr void __throw_if_valueless(_Vs&&... __vs) {
|
||||
const bool __valueless = (... || __vs.valueless_by_exception());
|
||||
_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr void
|
||||
__throw_if_valueless(_Vs&&... __vs) {
|
||||
const bool __valueless =
|
||||
(... || _VSTD::__as_variant(__vs).valueless_by_exception());
|
||||
if (__valueless) {
|
||||
__throw_bad_variant_access();
|
||||
__throw_bad_variant_access();
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Visitor, class... _Vs>
|
||||
template <
|
||||
class _Visitor, class... _Vs,
|
||||
typename = void_t<decltype(_VSTD::__as_variant(_VSTD::declval<_Vs>()))...> >
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
|
||||
constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
|
||||
_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr
|
||||
decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
|
||||
using __variant_detail::__visitation::__variant;
|
||||
_VSTD::__throw_if_valueless(_VSTD::forward<_Vs>(__vs)...);
|
||||
return __variant::__visit_value(_VSTD::forward<_Visitor>(__visitor),
|
||||
|
@ -1656,10 +1688,12 @@ constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
|
|||
}
|
||||
|
||||
#if _LIBCPP_STD_VER > 17
|
||||
template <class _Rp, class _Visitor, class... _Vs>
|
||||
template <
|
||||
class _Rp, class _Visitor, class... _Vs,
|
||||
typename = void_t<decltype(_VSTD::__as_variant(_VSTD::declval<_Vs>()))...> >
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
|
||||
constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs) {
|
||||
_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr _Rp
|
||||
visit(_Visitor&& __visitor, _Vs&&... __vs) {
|
||||
using __variant_detail::__visitation::__variant;
|
||||
_VSTD::__throw_if_valueless(_VSTD::forward<_Vs>(__vs)...);
|
||||
return __variant::__visit_value<_Rp>(_VSTD::forward<_Visitor>(__visitor),
|
||||
|
|
|
@ -160,7 +160,7 @@ __cpp_lib_type_trait_variable_templates 201510L <type_traits>
|
|||
__cpp_lib_uncaught_exceptions 201411L <exception>
|
||||
__cpp_lib_unordered_map_try_emplace 201411L <unordered_map>
|
||||
__cpp_lib_unwrap_ref 201811L <functional>
|
||||
__cpp_lib_variant 201606L <variant>
|
||||
__cpp_lib_variant 202102L <variant>
|
||||
__cpp_lib_void_t 201411L <type_traits>
|
||||
|
||||
*/
|
||||
|
@ -259,7 +259,7 @@ __cpp_lib_void_t 201411L <type_traits>
|
|||
# define __cpp_lib_type_trait_variable_templates 201510L
|
||||
# define __cpp_lib_uncaught_exceptions 201411L
|
||||
# define __cpp_lib_unordered_map_try_emplace 201411L
|
||||
# define __cpp_lib_variant 201606L
|
||||
# define __cpp_lib_variant 202102L
|
||||
# define __cpp_lib_void_t 201411L
|
||||
#endif
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// Test the feature test macros defined by <variant>
|
||||
|
||||
/* Constant Value
|
||||
__cpp_lib_variant 201606L [C++17]
|
||||
__cpp_lib_variant 202102L [C++17]
|
||||
*/
|
||||
|
||||
#include <variant>
|
||||
|
@ -39,8 +39,8 @@
|
|||
# ifndef __cpp_lib_variant
|
||||
# error "__cpp_lib_variant should be defined in c++17"
|
||||
# endif
|
||||
# if __cpp_lib_variant != 201606L
|
||||
# error "__cpp_lib_variant should have the value 201606L in c++17"
|
||||
# if __cpp_lib_variant != 202102L
|
||||
# error "__cpp_lib_variant should have the value 202102L in c++17"
|
||||
# endif
|
||||
|
||||
#elif TEST_STD_VER == 20
|
||||
|
@ -48,8 +48,8 @@
|
|||
# ifndef __cpp_lib_variant
|
||||
# error "__cpp_lib_variant should be defined in c++20"
|
||||
# endif
|
||||
# if __cpp_lib_variant != 201606L
|
||||
# error "__cpp_lib_variant should have the value 201606L in c++20"
|
||||
# if __cpp_lib_variant != 202102L
|
||||
# error "__cpp_lib_variant should have the value 202102L in c++20"
|
||||
# endif
|
||||
|
||||
#elif TEST_STD_VER > 20
|
||||
|
@ -57,8 +57,8 @@
|
|||
# ifndef __cpp_lib_variant
|
||||
# error "__cpp_lib_variant should be defined in c++2b"
|
||||
# endif
|
||||
# if __cpp_lib_variant != 201606L
|
||||
# error "__cpp_lib_variant should have the value 201606L in c++2b"
|
||||
# if __cpp_lib_variant != 202102L
|
||||
# error "__cpp_lib_variant should have the value 202102L in c++2b"
|
||||
# endif
|
||||
|
||||
#endif // TEST_STD_VER > 20
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
__cpp_lib_uncaught_exceptions 201411L [C++17]
|
||||
__cpp_lib_unordered_map_try_emplace 201411L [C++17]
|
||||
__cpp_lib_unwrap_ref 201811L [C++20]
|
||||
__cpp_lib_variant 201606L [C++17]
|
||||
__cpp_lib_variant 202102L [C++17]
|
||||
__cpp_lib_void_t 201411L [C++17]
|
||||
*/
|
||||
|
||||
|
@ -2090,8 +2090,8 @@
|
|||
# ifndef __cpp_lib_variant
|
||||
# error "__cpp_lib_variant should be defined in c++17"
|
||||
# endif
|
||||
# if __cpp_lib_variant != 201606L
|
||||
# error "__cpp_lib_variant should have the value 201606L in c++17"
|
||||
# if __cpp_lib_variant != 202102L
|
||||
# error "__cpp_lib_variant should have the value 202102L in c++17"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_void_t
|
||||
|
@ -3277,8 +3277,8 @@
|
|||
# ifndef __cpp_lib_variant
|
||||
# error "__cpp_lib_variant should be defined in c++20"
|
||||
# endif
|
||||
# if __cpp_lib_variant != 201606L
|
||||
# error "__cpp_lib_variant should have the value 201606L in c++20"
|
||||
# if __cpp_lib_variant != 202102L
|
||||
# error "__cpp_lib_variant should have the value 202102L in c++20"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_void_t
|
||||
|
@ -4491,8 +4491,8 @@
|
|||
# ifndef __cpp_lib_variant
|
||||
# error "__cpp_lib_variant should be defined in c++2b"
|
||||
# endif
|
||||
# if __cpp_lib_variant != 201606L
|
||||
# error "__cpp_lib_variant should have the value 201606L in c++2b"
|
||||
# if __cpp_lib_variant != 202102L
|
||||
# error "__cpp_lib_variant should have the value 202102L in c++2b"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_void_t
|
||||
|
|
|
@ -348,6 +348,86 @@ void test_caller_accepts_nonconst() {
|
|||
std::visit(Visitor{}, v);
|
||||
}
|
||||
|
||||
struct MyVariant : std::variant<short, long, float> {};
|
||||
|
||||
namespace std {
|
||||
template <size_t Index>
|
||||
void get(const MyVariant&) {
|
||||
assert(false);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
void test_derived_from_variant() {
|
||||
auto v1 = MyVariant{42};
|
||||
const auto cv1 = MyVariant{142};
|
||||
std::visit([](auto x) { assert(x == 42); }, v1);
|
||||
std::visit([](auto x) { assert(x == 142); }, cv1);
|
||||
std::visit([](auto x) { assert(x == -1.25f); }, MyVariant{-1.25f});
|
||||
std::visit([](auto x) { assert(x == 42); }, std::move(v1));
|
||||
std::visit([](auto x) { assert(x == 142); }, std::move(cv1));
|
||||
|
||||
// Check that visit does not take index nor valueless_by_exception members from the base class.
|
||||
struct EvilVariantBase {
|
||||
int index;
|
||||
char valueless_by_exception;
|
||||
};
|
||||
|
||||
struct EvilVariant1 : std::variant<int, long, double>,
|
||||
std::tuple<int>,
|
||||
EvilVariantBase {
|
||||
using std::variant<int, long, double>::variant;
|
||||
};
|
||||
|
||||
std::visit([](auto x) { assert(x == 12); }, EvilVariant1{12});
|
||||
std::visit([](auto x) { assert(x == 12.3); }, EvilVariant1{12.3});
|
||||
|
||||
// Check that visit unambiguously picks the variant, even if the other base has __impl member.
|
||||
struct ImplVariantBase {
|
||||
struct Callable {
|
||||
bool operator()();
|
||||
};
|
||||
|
||||
Callable __impl;
|
||||
};
|
||||
|
||||
struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
|
||||
using std::variant<int, long, double>::variant;
|
||||
};
|
||||
|
||||
std::visit([](auto x) { assert(x == 12); }, EvilVariant2{12});
|
||||
std::visit([](auto x) { assert(x == 12.3); }, EvilVariant2{12.3});
|
||||
}
|
||||
|
||||
struct any_visitor {
|
||||
template <typename T>
|
||||
void operator()(const T&) const {}
|
||||
};
|
||||
|
||||
template <typename T, typename = decltype(std::visit(
|
||||
std::declval<any_visitor&>(), std::declval<T>()))>
|
||||
constexpr bool has_visit(int) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_visit(...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void test_sfinae() {
|
||||
struct BadVariant : std::variant<short>, std::variant<long, float> {};
|
||||
struct BadVariant2 : private std::variant<long, float> {};
|
||||
struct GoodVariant : std::variant<long, float> {};
|
||||
struct GoodVariant2 : GoodVariant {};
|
||||
|
||||
static_assert(!has_visit<int>(0));
|
||||
static_assert(!has_visit<BadVariant>(0));
|
||||
static_assert(!has_visit<BadVariant2>(0));
|
||||
static_assert(has_visit<std::variant<int>>(0));
|
||||
static_assert(has_visit<GoodVariant>(0));
|
||||
static_assert(has_visit<GoodVariant2>(0));
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test_call_operator_forwarding();
|
||||
test_argument_forwarding();
|
||||
|
@ -355,6 +435,8 @@ int main(int, char**) {
|
|||
test_constexpr();
|
||||
test_exceptions();
|
||||
test_caller_accepts_nonconst();
|
||||
test_derived_from_variant();
|
||||
test_sfinae();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -411,6 +411,99 @@ void test_constexpr_explicit_side_effect() {
|
|||
static_assert(test_lambda(202) == 202, "");
|
||||
}
|
||||
|
||||
void test_derived_from_variant() {
|
||||
struct MyVariant : std::variant<short, long, float> {};
|
||||
|
||||
std::visit<bool>(
|
||||
[](auto x) {
|
||||
assert(x == 42);
|
||||
return true;
|
||||
},
|
||||
MyVariant{42});
|
||||
std::visit<bool>(
|
||||
[](auto x) {
|
||||
assert(x == -1.3f);
|
||||
return true;
|
||||
},
|
||||
MyVariant{-1.3f});
|
||||
|
||||
// Check that visit does not take index nor valueless_by_exception members from the base class.
|
||||
struct EvilVariantBase {
|
||||
int index;
|
||||
char valueless_by_exception;
|
||||
};
|
||||
|
||||
struct EvilVariant1 : std::variant<int, long, double>,
|
||||
std::tuple<int>,
|
||||
EvilVariantBase {
|
||||
using std::variant<int, long, double>::variant;
|
||||
};
|
||||
|
||||
std::visit<bool>(
|
||||
[](auto x) {
|
||||
assert(x == 12);
|
||||
return true;
|
||||
},
|
||||
EvilVariant1{12});
|
||||
std::visit<bool>(
|
||||
[](auto x) {
|
||||
assert(x == 12.3);
|
||||
return true;
|
||||
},
|
||||
EvilVariant1{12.3});
|
||||
|
||||
// Check that visit unambiguously picks the variant, even if the other base has __impl member.
|
||||
struct ImplVariantBase {
|
||||
struct Callable {
|
||||
bool operator()();
|
||||
};
|
||||
|
||||
Callable __impl;
|
||||
};
|
||||
|
||||
struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
|
||||
using std::variant<int, long, double>::variant;
|
||||
};
|
||||
|
||||
std::visit<bool>(
|
||||
[](auto x) {
|
||||
assert(x == 12);
|
||||
return true;
|
||||
},
|
||||
EvilVariant2{12});
|
||||
std::visit<bool>(
|
||||
[](auto x) {
|
||||
assert(x == 12.3);
|
||||
return true;
|
||||
},
|
||||
EvilVariant2{12.3});
|
||||
}
|
||||
|
||||
struct any_visitor {
|
||||
template <typename T>
|
||||
bool operator()(const T&) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename = decltype(std::visit<bool>(
|
||||
std::declval<any_visitor&>(), std::declval<T>()))>
|
||||
constexpr bool has_visit(int) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_visit(...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void test_sfinae() {
|
||||
struct BadVariant : std::variant<short>, std::variant<long, float> {};
|
||||
|
||||
static_assert(has_visit<std::variant<int> >(int()));
|
||||
static_assert(!has_visit<BadVariant>(int()));
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test_call_operator_forwarding<void>();
|
||||
test_argument_forwarding<void>();
|
||||
|
@ -425,6 +518,8 @@ int main(int, char**) {
|
|||
test_exceptions<int>();
|
||||
test_caller_accepts_nonconst<int>();
|
||||
test_constexpr_explicit_side_effect();
|
||||
test_derived_from_variant();
|
||||
test_sfinae();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -627,7 +627,7 @@ feature_test_macros = [ add_version_header(x) for x in [
|
|||
"headers": ["functional"],
|
||||
}, {
|
||||
"name": "__cpp_lib_variant",
|
||||
"values": { "c++17": 201606 },
|
||||
"values": { "c++17": 202102 },
|
||||
"headers": ["variant"],
|
||||
}, {
|
||||
"name": "__cpp_lib_void_t",
|
||||
|
|
Loading…
Reference in New Issue