libstdc++: Refactor _Variadic_union so _Unitialized<T, false> is not needed [PR112591].

The changes the _Variadic_union implementation, in a way that the
_Unitialized<T, false> partial specialization for non-trivial types is not
necessary.

This is simply done by separating the specialization for __trivially_destructible
being true and false, and for the later defining an empty destructor (similarly
as it was done using concepts).

We also reduce the number of specialization of _Variadic_union, so specialization
(int, int) is reused by (string, int, int) and (int, int). This is done by
initialization __trivially_destructible with conjunction of
is_trivially_destructible_v for remaining components. This is only necessary
for non-trivial (false) specialization, as if both _First and _Rest... are
trivially destructible, then _Rest must also be.

The above change does not regress the fix r14-7259-g2d55d94e5df389 for
template depth, and both before and after the change template depth is 266.
I have added dg-options to the 87619.cc to catch future regressions.

This also add test for PR112591.

	PR libstdc++/112591

libstdc++-v3/ChangeLog:

	* include/std/variant (_Variadic_union): Separate specializations for
	for union of only trivially destructible types (true as first template
	argument). Unconditionally define destructor for _Variadic_union<false,
	_First, _Rest...>.
	* testsuite/20_util/variant/87619.cc: Add limit for the template depth.
	* testsuite/20_util/variant/112591.cc: New test.

Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
Tomasz Kamiński 2025-12-08 08:49:45 +01:00
parent 28f12e889c
commit 09bece00d0
3 changed files with 59 additions and 9 deletions

View File

@ -393,8 +393,29 @@ namespace __variant
_Variadic_union(in_place_index_t<_Np>, _Args&&...) = delete;
};
template<bool __trivially_destructible, typename _First, typename... _Rest>
union _Variadic_union<__trivially_destructible, _First, _Rest...>
template<typename _First, typename... _Rest>
union _Variadic_union<true, _First, _Rest...>
{
constexpr _Variadic_union() : _M_rest() { }
template<typename... _Args>
constexpr
_Variadic_union(in_place_index_t<0>, _Args&&... __args)
: _M_first(in_place_index<0>, std::forward<_Args>(__args)...)
{ }
template<size_t _Np, typename... _Args>
constexpr
_Variadic_union(in_place_index_t<_Np>, _Args&&... __args)
: _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...)
{ }
_Uninitialized<_First> _M_first;
_Variadic_union<true, _Rest...> _M_rest;
};
template<typename _First, typename... _Rest>
union _Variadic_union<false, _First, _Rest...>
{
constexpr _Variadic_union() : _M_rest() { }
@ -410,24 +431,19 @@ namespace __variant
: _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...)
{ }
#if __cpp_lib_variant >= 202106L
_Variadic_union(const _Variadic_union&) = default;
_Variadic_union(_Variadic_union&&) = default;
_Variadic_union& operator=(const _Variadic_union&) = default;
_Variadic_union& operator=(_Variadic_union&&) = default;
~_Variadic_union() = default;
// If any alternative type is not trivially destructible then we need a
// user-provided destructor that does nothing. The active alternative
// will be destroyed by _Variant_storage::_M_reset() instead of here.
constexpr ~_Variadic_union()
requires (!__trivially_destructible)
_GLIBCXX20_CONSTEXPR ~_Variadic_union()
{ }
#endif
_Uninitialized<_First> _M_first;
_Variadic_union<__trivially_destructible, _Rest...> _M_rest;
_Variadic_union<(is_trivially_destructible_v<_Rest> && ...), _Rest...> _M_rest;
};
// _Never_valueless_alt is true for variant alternatives that can

View File

@ -0,0 +1,32 @@
// { dg-do run { target c++17 } }
#include <variant>
#include <testsuite_hooks.h>
struct NonEmpty { int x; };
struct TrivialEmpty {};
struct NonTrivialEmpty { ~NonTrivialEmpty() {} };
template<typename T>
struct Compose : T
{
std::variant<T, int> v;
};
template<typename T>
bool testAlias()
{
Compose<T> c;
return static_cast<T*>(&c) == &std::get<T>(c.v);
}
int main()
{
VERIFY( !testAlias<NonEmpty>() );
VERIFY( !testAlias<TrivialEmpty>() );
#if __cplusplus >= 202002L
VERIFY( !testAlias<NonTrivialEmpty>() );
#else
VERIFY( testAlias<NonTrivialEmpty>() );
#endif
}

View File

@ -16,6 +16,7 @@
// <http://www.gnu.org/licenses/>.
// { dg-do compile { target c++17 } }
// { dg-options "-ftemplate-depth=270" }
#include <variant>
#include <utility>
@ -23,6 +24,7 @@
template<std::size_t I>
struct S {
~S() {}
};
template <std::size_t... Is>