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
"`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","",""
"","","","","",""
------------------------------------------------- -----------------
``__cpp_lib_unordered_map_try_emplace`` ``201411L``
------------------------------------------------- -----------------
- ``__cpp_lib_variant`` ``201606L``
+ ``__cpp_lib_variant`` ``202102L``
------------------------------------------------- -----------------
``__cpp_lib_void_t`` ``201411L``
------------------------------------------------- -----------------
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>
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>
__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
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),
}
#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),
__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>
*/
# 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
// Test the feature test macros defined by <variant>
/* Constant Value
- __cpp_lib_variant 201606L [C++17]
+ __cpp_lib_variant 202102L [C++17]
*/
#include <variant>
# 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
# 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
# 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
__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]
*/
# 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
# 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
# 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
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();
test_constexpr();
test_exceptions();
test_caller_accepts_nonconst();
+ test_derived_from_variant();
+ test_sfinae();
return 0;
}
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>();
test_exceptions<int>();
test_caller_accepts_nonconst<int>();
test_constexpr_explicit_side_effect();
+ test_derived_from_variant();
+ test_sfinae();
return 0;
}
"headers": ["functional"],
}, {
"name": "__cpp_lib_variant",
- "values": { "c++17": 201606 },
+ "values": { "c++17": 202102 },
"headers": ["variant"],
}, {
"name": "__cpp_lib_void_t",