This commit implements P2819R2 for C++26, making std::complex
destructurable and tuple-like (see [complex.tuple]).
std::get needs to get forward declared in stl_pair.h (following the
existing precedent for the implementation of P2165R4, cf.
r14-8710-g65b4cba9d6a9ff), and implemented in <complex>.
Also, std::get(complex<T>) needs to return *references* to the real and
imaginary parts of a std::complex object, honoring the value category
and constness of the argument. In principle a straightforward task, it
gets a bit convoluted by the fact that:
1) std::complex does not have existing getters that one can use for this
(real() and imag() return values, not references);
2) there are specializations for language/extended floating-point types,
which requires some duplication -- need to amend the primary and all
the specializations;
3) these specializations use a `__complex__ T`, but the primary template
uses two non-static data members, making generic code harder to write.
The implementation choice used here is to add the overloads of std::get
for complex as declared in [complex.tuple]. In turn they dispatch to a
newly added getter that extracts references to the real/imaginary parts
of a complex<T>. This getter is private API, and the implementation
depends on whether it's the primary (bind the data member) or a
specialization (use the GCC language extensions for __complex__).
To avoid duplication and minimize template instantiations, the getter
uses C++23's deducing this (this avoids const overloads). The value
category is dealt with by the std::get overloads.
Add a test that covers the aspects of the tuple protocol, as well as the
tuple-like interface. While at it, add a test for the existing
tuple-like feature-testing macro.
PR libstdc++/113310
libstdc++-v3/ChangeLog:
* include/bits/stl_pair.h (get): Forward-declare std::get for
std::complex.
* include/bits/version.def (tuple_like): Bump the value of
the feature-testing macro in C++26.
* include/bits/version.h: Regenerate.
* include/std/complex: Implement the tuple protocol for
std::complex.
(tuple_size): Specialize for std::complex.
(tuple_element): Ditto.
(__is_tuple_like_v): Ditto.
(complex): Add a private getter to obtain references to the real
and the imaginary part, on the primary class template and on its
specializations.
(get): Add overloads of std::get for std::complex.
* testsuite/20_util/tuple/tuple_like_ftm.cc: New test.
* testsuite/26_numerics/complex/tuple_like.cc: New test.
template<size_t...>
struct _Index_tuple;
+ template<typename _Tp>
+ class complex;
+
template<size_t _Int, class _Tp1, class _Tp2>
constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&
get(pair<_Tp1, _Tp2>& __in) noexcept;
constexpr const _Tp&&
get(const array<_Tp, _Nm>&&) noexcept;
+#if __glibcxx_tuple_like >= 202311 // >= C++26
+ template<size_t _Int, typename _Tp>
+ constexpr _Tp&
+ get(complex<_Tp>&) noexcept;
+ template<size_t _Int, typename _Tp>
+ constexpr _Tp&&
+ get(complex<_Tp>&&) noexcept;
+ template<size_t _Int, typename _Tp>
+ constexpr const _Tp&
+ get(const complex<_Tp>&) noexcept;
+ template<size_t _Int, typename _Tp>
+ constexpr const _Tp&&
+ get(const complex<_Tp>&&) noexcept;
+#endif
+
#if ! __cpp_lib_concepts
// Concept utility functions, reused in conditionally-explicit
// constructors.
ftms = {
name = tuple_like;
+ values = {
+ v = 202311;
+ cxxmin = 26;
+ extra_cond = "__cpp_explicit_this_parameter >= 202110L";
+ };
values = {
v = 202207;
cxxmin = 23;
#undef __glibcxx_want_to_underlying
#if !defined(__cpp_lib_tuple_like)
-# if (__cplusplus >= 202100L)
+# if (__cplusplus > 202302L) && (__cpp_explicit_this_parameter >= 202110L)
+# define __glibcxx_tuple_like 202311L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_tuple_like)
+# define __cpp_lib_tuple_like 202311L
+# endif
+# elif (__cplusplus >= 202100L)
# define __glibcxx_tuple_like 202207L
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_tuple_like)
# define __cpp_lib_tuple_like 202207L
#define __glibcxx_want_constexpr_complex
#define __glibcxx_want_complex_udls
+#define __glibcxx_want_tuple_like
#include <bits/version.h>
+#if __glibcxx_tuple_like >= 202311 // >= C++26
+# include <bits/utility.h> // for tuple_element_t
+# include <bits/stl_pair.h> // for __is_tuple_like_v
+#endif
+
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/// Return complex hyperbolic tangent of @a z.
template<typename _Tp> complex<_Tp> tanh(const complex<_Tp>&);
+#if __glibcxx_tuple_like >= 202311L // >= C++26
+ template<typename _Tp>
+ struct tuple_size<complex<_Tp>>
+ : public integral_constant<size_t, 2> { };
+ template<typename _Tp>
+ struct tuple_element<0, complex<_Tp>>
+ { using type = _Tp; };
+ template<typename _Tp>
+ struct tuple_element<1, complex<_Tp>>
+ { using type = _Tp; };
+ template<typename _Tp>
+ inline constexpr bool __is_tuple_like_v<complex<_Tp>> = true;
+#endif // __glibcxx_tuple_like >= 202311
// 26.2.2 Primary template class complex
/**
_GLIBCXX_CONSTEXPR complex __rep() const
{ return *this; }
+#if __glibcxx_tuple_like >= 202311L // >= C++26
+ template<typename _Cp>
+ [[__gnu__::__always_inline__]]
+ constexpr auto&
+ __get_part(this _Cp& __z, size_t __i) noexcept
+ {
+ return __i == 0 ? __z._M_real : __z._M_imag;
+ }
+#endif
+
private:
_Tp _M_real;
_Tp _M_imag;
{ return __z.imag(); }
#endif
+#if __glibcxx_tuple_like >= 202311L // >= C++26
+ template<size_t _Int, typename _Tp>
+ [[nodiscard,__gnu__::__always_inline__]]
+ constexpr _Tp&
+ get(complex<_Tp>& __z) noexcept
+ {
+ static_assert(_Int < 2);
+ return __z.__get_part(_Int);
+ }
+
+ template<size_t _Int, typename _Tp>
+ [[nodiscard,__gnu__::__always_inline__]]
+ constexpr _Tp&&
+ get(complex<_Tp>&& __z) noexcept
+ {
+ static_assert(_Int < 2);
+ return std::move(__z.__get_part(_Int));
+ }
+
+ template<size_t _Int, typename _Tp>
+ [[nodiscard,__gnu__::__always_inline__]]
+ constexpr const _Tp&
+ get(const complex<_Tp>& __z) noexcept
+ {
+ static_assert(_Int < 2);
+ return __z.__get_part(_Int);
+ }
+
+ template<size_t _Int, typename _Tp>
+ [[nodiscard,__gnu__::__always_inline__]]
+ constexpr const _Tp&&
+ get(const complex<_Tp>&& __z) noexcept
+ {
+ static_assert(_Int < 2);
+ return std::move(__z.__get_part(_Int));
+ }
+#endif // __glibcxx_tuple_like >= 202311
+
#if _GLIBCXX_USE_C99_COMPLEX
#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
inline _Float16
: std::pow(complex<_Tp>(__x), __y);
}
+#if __glibcxx_tuple_like >= 202311L // >= C++26
+#define _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR \
+ template<typename _Cp> \
+ [[__gnu__::__always_inline__]] \
+ constexpr auto& \
+ __get_part(this _Cp& __z, size_t __i) noexcept \
+ { \
+ return __i == 0 ? __real__ __z._M_value \
+ : __imag__ __z._M_value; \
+ }
+#else
+#define _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+#endif
+
/// 26.2.3 complex specializations
/// complex<float> specialization
template<>
_GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; }
+ _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
private:
_ComplexT _M_value;
};
_GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; }
+ _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
private:
_ComplexT _M_value;
};
_GLIBCXX_CONSTEXPR _ComplexT __rep() const { return _M_value; }
+ _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
private:
_ComplexT _M_value;
};
constexpr _ComplexT __rep() const { return _M_value; }
+ _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
private:
_ComplexT _M_value;
};
#endif
+#undef _GLIBCXX26_DECLARE_COMPLEX_TUPLE_HELPER_ACCESSOR
+
#if __cplusplus <= 202002L
// These bits have to be at the end of this file, so that the
// specializations have all been defined.
--- /dev/null
+// { dg-do preprocess { target c++23 } }
+// { dg-add-options no_pch }
+
+#include <utility>
+
+#if !defined(__cpp_lib_tuple_like)
+# error "Feature-test macro for tuple-like is missing"
+#elif __cplusplus > 202302L
+# if __cpp_lib_tuple_like < 202311L
+# error "Feature-test macro for tuple-like has wrong value"
+# endif
+#else
+# if __cpp_lib_tuple_like < 202207L
+# error "Feature-test macro for tuple-like has wrong value"
+# endif
+#endif
+
--- /dev/null
+// { dg-do compile { target c++26 } }
+
+#include <complex>
+#include <ranges>
+#include <string>
+#include <type_traits>
+#include <tuple>
+#include <utility>
+
+#include <testsuite_hooks.h>
+
+template <typename T>
+constexpr
+void
+test_sanity()
+{
+ using C = std::complex<T>;
+
+ static_assert(std::tuple_size_v<C> == 2);
+ static_assert(std::is_same_v<std::tuple_element_t<0, C>, T>);
+ static_assert(std::is_same_v<std::tuple_element_t<1, C>, T>);
+
+ static_assert(std::is_same_v<decltype(get<0>(std::declval<C&>())), T&>);
+ static_assert(std::is_same_v<decltype(get<1>(std::declval<C&>())), T&>);
+ static_assert(std::is_same_v<decltype(get<0>(std::declval<const C&>())), const T&>);
+ static_assert(std::is_same_v<decltype(get<1>(std::declval<const C&>())), const T&>);
+ static_assert(std::is_same_v<decltype(get<0>(std::declval<C>())), T&&>);
+ static_assert(std::is_same_v<decltype(get<1>(std::declval<C>())), T&&>);
+ static_assert(std::is_same_v<decltype(get<0>(std::declval<const C>())), const T&&>);
+ static_assert(std::is_same_v<decltype(get<1>(std::declval<const C>())), const T&&>);
+}
+
+template <typename T>
+constexpr
+void
+test_get()
+{
+ using C = std::complex<T>;
+
+ C cpx(T(1), T(2));
+ VERIFY(std::get<0>(cpx) == T(1));
+ VERIFY(std::get<1>(cpx) == T(2));
+
+ const C cpx2(T(3), T(4));
+ VERIFY(std::get<0>(cpx2) == T(3));
+ VERIFY(std::get<1>(cpx2) == T(4));
+
+ struct derived : public C { using C::C; };
+ derived cpx3(T(5), T(6));
+ VERIFY(std::get<0>(cpx3) == T(5));
+ VERIFY(std::get<1>(cpx3) == T(6));
+}
+
+template <typename T>
+constexpr
+void
+test_structured_binding()
+{
+ using C = std::complex<T>;
+ C cpx(T(1), T(2));
+
+ auto& [r, i] = cpx;
+ VERIFY(r == T(1));
+ VERIFY(i == T(2));
+
+ r = T(3);
+ VERIFY(cpx.real() == T(3));
+ VERIFY(cpx.imag() == T(2));
+
+ i = T(4);
+ VERIFY(cpx.real() == T(3));
+ VERIFY(cpx.imag() == T(4));
+
+ const C cpx2(T(5), T(6));
+ auto& [r2, i2] = cpx2;
+ VERIFY(r2 == T(5));
+ VERIFY(i2 == T(6));
+}
+
+template <typename T>
+constexpr
+void
+test_tuple_cat()
+{
+ std::complex<T> cpx(T(1), T(2));
+ std::pair<int, std::string> p(42, "hello");
+
+ auto r = std::tuple_cat(cpx, p, cpx);
+ static_assert(std::is_same_v<decltype(r), std::tuple<T, T, int, std::string, T, T>>);
+ VERIFY(std::get<0>(r) == T(1));
+ VERIFY(std::get<1>(r) == T(2));
+ VERIFY(std::get<2>(r) == 42);
+ VERIFY(std::get<3>(r) == "hello");
+ VERIFY(std::get<4>(r) == T(1));
+ VERIFY(std::get<5>(r) == T(2));
+}
+
+template <typename T>
+constexpr
+void
+test_element_view()
+{
+ std::complex<T> array[5] = {
+ { T(0), T(1) },
+ { T(2), T(3) },
+ { T(4), T(5) },
+ { T(6), T(7) },
+ { T(8), T(9) }
+ };
+
+ T real_reduction = std::ranges::fold_left(array | std::views::keys, {}, std::plus{});
+ VERIFY(real_reduction == T(20));
+
+ T imag_reduction = std::ranges::fold_left(array | std::views::values, {}, std::plus{});
+ VERIFY(imag_reduction == T(25));
+}
+
+template <typename T>
+constexpr
+void
+test_apply()
+{
+ std::complex<T> cpx(T(1), T(2));
+
+ auto f = [](T a, T b) { return a + b; };
+ auto result = std::apply(f, cpx);
+
+ VERIFY(result == T(3));
+}
+
+template <typename T>
+constexpr
+bool
+all_tests()
+{
+ test_sanity<T>();
+ test_structured_binding<T>();
+ test_tuple_cat<T>();
+ test_element_view<T>();
+ test_apply<T>();
+ test_get<T>();
+ return true;
+}
+
+#define TEST(T) \
+ static_assert(all_tests<T>()); \
+ template T& std::get<0, T>(std::complex<T>&); \
+ template T& std::get<1, T>(std::complex<T>&); \
+ template T&& std::get<0, T>(std::complex<T>&&); \
+ template T&& std::get<1, T>(std::complex<T>&&); \
+ template const T& std::get<0, T>(const std::complex<T>&); \
+ template const T& std::get<1, T>(const std::complex<T>&); \
+ template const T&& std::get<0, T>(const std::complex<T>&&); \
+ template const T&& std::get<1, T>(const std::complex<T>&&); \
+
+TEST(float)
+TEST(double)
+TEST(long double)
+
+#ifdef __STDCPP_FLOAT16_T__
+TEST(_Float16)
+#endif
+#ifdef __STDCPP_FLOAT32_T__
+TEST(_Float32)
+#endif
+#ifdef __STDCPP_FLOAT64_T__
+TEST(_Float64)
+#endif
+#ifdef __STDCPP_FLOAT128_T__
+TEST(_Float128)
+#endif
+#ifdef __STDCPP_BFLOAT16_T__
+TEST(__gnu_cxx::__bfloat16_t)
+#endif
+
+TEST(char)
+TEST(int)
+TEST(unsigned int)
+TEST(size_t)