ad820b0bb5
This implements the changes in P2231R1 which make std::variant fully constexpr in C++20. We need to replace placement new with std::construct_at, but that isn't defined for C++17. Use std::_Construct instead, which forwards to std::construct_at in C++20 mode (since the related changes to make std::optional fully constexpr, in r12-4389). We also need to replace the untyped char buffer in _Uninitialized with a union, which can be accessed in constexpr functions. But the union needs to have a non-trivial destructor if its variant type is non-trivial, which means that the _Variadic_union also needs a non-trivial destructor. This adds a constrained partial specialization of _Variadic_union for the C++20-only case where a non-trivial destructor is needed. We can't use concepts to constrain the specialization (or the primary template's destructor) in C++17, so retain the untyped char buffer solution for C++17 mode. libstdc++-v3/ChangeLog: * include/std/variant (__cpp_lib_variant): Update value for C++20. (__variant_cast, __variant_construct): Add constexpr for C++20. (__variant_construct_single, __construct_by_index) Likewise. Use std::_Construct instead of placement new. (_Uninitialized<T, false>) [__cplusplus >= 202002]: Replace buffer with a union and define a destructor. (_Variadic_union) [__cplusplus >= 202002]: Add a specialization for non-trivial destruction. (_Variant_storage::__index_of): New helper variable template. (_Variant_storage::~_Variant_storage()): Add constexpr. (_Variant_storage::_M_reset()): Likewise. (_Copy_ctor_base, _Move_ctor_base): Likewise. (_Copy_assign_base, _Move_assign_base): Likewise. (variant, swap): Likewise. * include/std/version (__cpp_lib_variant): Update value for C++20. * testsuite/20_util/optional/version.cc: Check for exact value in C++17. * testsuite/20_util/variant/87619.cc: Increase timeout for C++20 mode. * testsuite/20_util/variant/constexpr.cc: New test. * testsuite/20_util/variant/version.cc: New test.
139 lines
3.1 KiB
C++
139 lines
3.1 KiB
C++
// { dg-options "-std=gnu++20" }
|
|
// { dg-do compile { target c++20 } }
|
|
|
|
#include <variant>
|
|
|
|
// P2231R1 Missing constexpr in std::optional and std::variant
|
|
|
|
#ifndef __cpp_lib_variant
|
|
#error "Feature test macro for variant is missing in <variant>"
|
|
#elif __cpp_lib_variant < 202106L
|
|
# error "Feature test macro for variant has wrong value for C++20 in <variant>"
|
|
#endif
|
|
|
|
#include <testsuite_hooks.h>
|
|
|
|
|
|
constexpr bool
|
|
test_assign()
|
|
{
|
|
std::variant<int, double> v1(1);
|
|
v1 = 2.5;
|
|
VERIFY( std::get<double>(v1) == 2.5 );
|
|
|
|
v1 = 99;
|
|
VERIFY( std::get<int>(v1) == 99 );
|
|
v1 = 999;
|
|
VERIFY( std::get<int>(v1) == 999 );
|
|
|
|
struct S // non-trivial special members
|
|
{
|
|
constexpr S(int i) : i(i) { }
|
|
constexpr ~S() { }
|
|
constexpr S(const S& s) : i(s.i) { }
|
|
|
|
int i;
|
|
};
|
|
|
|
std::variant<int, S> v;
|
|
v = S(123);
|
|
VERIFY( std::get<1>(v).i == 123 );
|
|
|
|
const S s(456);
|
|
v = s;
|
|
VERIFY( std::get<1>(v).i == 456 );
|
|
|
|
v = 789;
|
|
VERIFY( std::get<0>(v) == 789 );
|
|
|
|
return true;
|
|
}
|
|
|
|
static_assert( test_assign() );
|
|
|
|
constexpr bool
|
|
test_emplace()
|
|
{
|
|
struct S // non-trivial special members
|
|
{
|
|
constexpr S(std::initializer_list<int> l) : i(l.begin()[0]) { }
|
|
constexpr S(std::initializer_list<int> l, int n) : i(l.begin()[n]) { }
|
|
constexpr ~S() { }
|
|
constexpr S(const S& s) : i(s.i) { }
|
|
|
|
int i;
|
|
};
|
|
|
|
std::variant<int, double, S> v(1);
|
|
|
|
// template<class T, class... Args> constexpr T& emplace(Args&&... args);
|
|
v.emplace<double>(2.0);
|
|
VERIFY( std::get<1>(v) == 2.0 );
|
|
v.emplace<double>(2.5);
|
|
VERIFY( std::get<1>(v) == 2.5 );
|
|
v.emplace<int>(2.5);
|
|
VERIFY( std::get<0>(v) == 2 );
|
|
|
|
// template<class T, class U, class... Args>
|
|
// constexpr T& emplace(initializer_list<U>, Args&&... args);
|
|
v.emplace<S>({3, 2, 1});
|
|
VERIFY( std::get<2>(v).i == 3 );
|
|
v.emplace<S>({3, 2, 1}, 1);
|
|
VERIFY( std::get<2>(v).i == 2 );
|
|
|
|
// template<size_t I, class... Args>
|
|
// constexpr variant_alternative_t<I, ...>& emplace(Args&&... args);
|
|
v.emplace<1>(3.0);
|
|
VERIFY( std::get<1>(v) == 3.0 );
|
|
v.emplace<1>(0.5);
|
|
VERIFY( std::get<1>(v) == 0.5 );
|
|
v.emplace<0>(1.5);
|
|
VERIFY( std::get<0>(v) == 1 );
|
|
|
|
// template<size_t I, class U, class... Args>
|
|
// constexpr variant_alternative_t<I, ...>&
|
|
// emplace(initializer_list<U>, Args&&... args);
|
|
v.emplace<2>({7, 8, 9});
|
|
VERIFY( std::get<2>(v).i == 7 );
|
|
v.emplace<2>({13, 12, 11}, 1);
|
|
VERIFY( std::get<2>(v).i == 12 );
|
|
|
|
return true;
|
|
}
|
|
|
|
static_assert( test_emplace() );
|
|
|
|
constexpr bool
|
|
test_swap()
|
|
{
|
|
std::variant<int, double> v1(1), v2(2.5);
|
|
v1.swap(v2);
|
|
VERIFY( std::get<double>(v1) == 2.5 );
|
|
VERIFY( std::get<int>(v2) == 1 );
|
|
|
|
swap(v1, v2);
|
|
VERIFY( std::get<int>(v1) == 1 );
|
|
VERIFY( std::get<double>(v2) == 2.5 );
|
|
|
|
struct S
|
|
{
|
|
constexpr S(int i) : i(i) { }
|
|
constexpr S(S&& s) : i(s.i) { }
|
|
constexpr S& operator=(S&& s) { i = s.i; s.i = -1; return *this; }
|
|
|
|
int i;
|
|
};
|
|
|
|
std::variant<int, S> v3(3), v4(S(4));
|
|
v3.swap(v4);
|
|
VERIFY( std::get<S>(v3).i == 4 );
|
|
VERIFY( std::get<int>(v4) == 3 );
|
|
v3.swap(v4);
|
|
VERIFY( std::get<int>(v3) == 3 );
|
|
VERIFY( std::get<S>(v4).i == 4 );
|
|
|
|
return true;
|
|
}
|
|
|
|
static_assert( test_swap() );
|