Hello,The attached patch adds the span(initializer_list) constructor, added by P2447R6 for C++26.
It seems that GCC is somewhat aggressive with its -Winit-list-lifetime warning, which actively interferes with this feature. The idea of the new constructor is to allow calls like:
void f(std::span<const int>); f({1, 2, 3});which is completely fine as the lifetime of the initializer_list encompasses the one of the std::span parameter. However GCC complains about the risk of dangling here. I've therefore disabled the warning for the new constructor.
Thanks, -- Giuseppe D'Angelo
From bb1d537ee7b68883403127903834427c6787c505 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo <giuseppe.dang...@kdab.com> Date: Tue, 3 Dec 2024 16:56:45 +0100 Subject: [PATCH] libstdc++: add initializer_list constructor to std::span (P2447) This commit implements P2447R6. The code is straightforward (just one extra constructor, with constraints and conditional explicit). I decided to suppress -Winit-list-lifetime because otherwise it would give too many false positives. The new constructor is meant to be used as a parameter-passing interface (this is a design choice, see P2447R6/2) and, as such, the initializer_list won't dangle despite GCC's warnings. The new constructor isn't 100% backwards compatible. A couple of examples are included in Annex C, but I have also lifted some more from R4. A new test checks for the old and the new behaviors. libstdc++-v3/ChangeLog: * include/bits/version.def: Added the new feature-testing macro. * include/bits/version.h (defined): Regenerated. * include/std/span: Added constructor from initializer_list. * testsuite/23_containers/span/init_list_cons.cc: New test. * testsuite/23_containers/span/init_list_cons_neg.cc: New test. --- libstdc++-v3/include/bits/version.def | 8 +++ libstdc++-v3/include/bits/version.h | 10 +++ libstdc++-v3/include/std/span | 21 ++++++ .../23_containers/span/init_list_cons.cc | 65 +++++++++++++++++++ .../23_containers/span/init_list_cons_neg.cc | 31 +++++++++ 5 files changed, 135 insertions(+) create mode 100644 libstdc++-v3/testsuite/23_containers/span/init_list_cons.cc create mode 100644 libstdc++-v3/testsuite/23_containers/span/init_list_cons_neg.cc diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 8d4b8e9b383..cfa0469fb2d 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1853,6 +1853,14 @@ ftms = { }; }; +ftms = { + name = span_initializer_list; + values = { + v = 202311; + cxxmin = 26; + }; +}; + ftms = { name = text_encoding; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index c556aca38fa..6a2c66bdf81 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2055,6 +2055,16 @@ #endif /* !defined(__cpp_lib_saturation_arithmetic) && defined(__glibcxx_want_saturation_arithmetic) */ #undef __glibcxx_want_saturation_arithmetic +#if !defined(__cpp_lib_span_initializer_list) +# if (__cplusplus > 202302L) +# define __glibcxx_span_initializer_list 202311L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_span_initializer_list) +# define __cpp_lib_span_initializer_list 202311L +# endif +# endif +#endif /* !defined(__cpp_lib_span_initializer_list) && defined(__glibcxx_want_span_initializer_list) */ +#undef __glibcxx_want_span_initializer_list + #if !defined(__cpp_lib_text_encoding) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (_GLIBCXX_USE_NL_LANGINFO_L) # define __glibcxx_text_encoding 202306L diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span index f1c19b58737..b84ac87b657 100644 --- a/libstdc++-v3/include/std/span +++ b/libstdc++-v3/include/std/span @@ -39,6 +39,7 @@ #endif #define __glibcxx_want_span +#define __glibcxx_want_span_initializer_list #include <bits/version.h> #ifdef __cpp_lib_span // C++ >= 20 && concepts @@ -46,6 +47,9 @@ #include <cstddef> #include <bits/stl_iterator.h> #include <bits/ranges_base.h> +#ifdef __cpp_lib_span_initializer_list +# include <initializer_list> +#endif namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -226,6 +230,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } +#if __cpp_lib_span_initializer_list >= 202311L +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winit-list-lifetime" + constexpr + explicit(extent != dynamic_extent) + span(initializer_list<value_type> __il) + requires (is_const_v<_Type>) + : _M_extent(__il.size()), _M_ptr(__il.begin()) + { + if constexpr (extent != dynamic_extent) + { + __glibcxx_assert(__il.size() == extent); + } + } +#pragma GCC diagnostic pop +#endif + constexpr span(const span&) noexcept = default; diff --git a/libstdc++-v3/testsuite/23_containers/span/init_list_cons.cc b/libstdc++-v3/testsuite/23_containers/span/init_list_cons.cc new file mode 100644 index 00000000000..7aabbf1e809 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/span/init_list_cons.cc @@ -0,0 +1,65 @@ +// { dg-do compile { target c++26 } } + +#include <span> +#include <type_traits> + +#if !defined(__cpp_lib_span_initializer_list) +# error "__cpp_lib_span_initializer_list should be defined" +#elif __cpp_lib_span_initializer_list < 202311L +# error "Wrong value for __cpp_lib_span_initializer_list (should be >= 202311L)" +#endif + +// Check the constraint on the initialier_list constructor +static_assert( std::is_const_v<std::span<const int>::element_type>); +static_assert(!std::is_const_v<std::span< int>::element_type>); + +static_assert( std::is_constructible_v<std::span<const int >, std::initializer_list< int>>); +static_assert( std::is_constructible_v<std::span<const int >, std::initializer_list<const int>>); +static_assert( std::is_constructible_v<std::span<const int, 42>, std::initializer_list< int>>); +static_assert( std::is_constructible_v<std::span<const int, 42>, std::initializer_list<const int>>); +static_assert(!std::is_constructible_v<std::span< int >, std::initializer_list< int>>); +static_assert(!std::is_constructible_v<std::span< int >, std::initializer_list<const int>>); +static_assert(!std::is_constructible_v<std::span< int, 42>, std::initializer_list< int>>); +static_assert(!std::is_constructible_v<std::span< int, 42>, std::initializer_list<const int>>); + +// Check the explicit-ness on the initialier_list constructor +static_assert( std::is_convertible_v<std::initializer_list< int>, std::span<const int >>); +static_assert( std::is_convertible_v<std::initializer_list<const int>, std::span<const int >>); +static_assert(!std::is_convertible_v<std::initializer_list< int>, std::span<const int, 42>>); +static_assert(!std::is_convertible_v<std::initializer_list<const int>, std::span<const int, 42>>); +static_assert(!std::is_convertible_v<std::initializer_list< int>, std::span< int >>); +static_assert(!std::is_convertible_v<std::initializer_list<const int>, std::span< int >>); +static_assert(!std::is_convertible_v<std::initializer_list< int>, std::span< int, 42>>); +static_assert(!std::is_convertible_v<std::initializer_list<const int>, std::span< int, 42>>); + +constexpr size_t fun1(std::span<const int> s) +{ + return s.size(); +} + +static_assert(fun1({}) == 0); +static_assert(fun1({1, 2, 3}) == 3); +static_assert(fun1(std::initializer_list<int>{1, 2, 3}) == 3); + +// Stress-test array->pointer decays +struct decayer { + constexpr decayer() = default; + constexpr decayer(decayer *) {} +}; + +constexpr size_t fun2(std::span<const decayer> s) +{ + return s.size(); +} + +void test01() +{ + int intArray[42]; + static_assert(fun1(intArray) == 42); + + decayer decArray[42]; + static_assert(fun2(decArray) == 42); + static_assert(fun2({decArray}) == 1); // decayer[] -> decayer* -> decayer(decayer*) -> init_list<decayer> of 1 element + static_assert(fun2({decArray, decArray + 42}) == 2); // does not select span(iterator, iterator) + static_assert(fun2({decArray, decArray, decArray}) == 3); +} diff --git a/libstdc++-v3/testsuite/23_containers/span/init_list_cons_neg.cc b/libstdc++-v3/testsuite/23_containers/span/init_list_cons_neg.cc new file mode 100644 index 00000000000..ef43541b769 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/span/init_list_cons_neg.cc @@ -0,0 +1,31 @@ +// { dg-do run { target c++20 } } + +#include <span> +#include <utility> +#include <any> + +#include <testsuite_hooks.h> + +// Examples from P2447R4 +void one(std::pair<int, int>) {} +void one(std::span<const int>) {} +void two(std::span<const int, 2>) {} +constexpr std::size_t three(std::span<void * const> v) { return v.size(); } +constexpr std::size_t four(std::span<const std::any> v) { return v.size(); } + +int main() +{ + one({1, 2}); // { dg-error "call of overloaded" "should be ambiguous with the one(std::pair) overload" { target c++26 } } + two({{1, 2}}); // { dg-error "would use explicit constructor" "should prefer the initializer_list constructor, which is explicit" { target c++26 } } + + void *array3[10]; + std::any array4[10]; + +#if __cpp_lib_span_initializer_list + static_assert(three({array3, 0}) == 2); + VERIFY(four({array4, array4 + 10}) == 2); +#else + static_assert(three({array3, 0}) == 0); + VERIFY(four({array4, array4 + 10}) == 10); +#endif +} -- 2.34.1
smime.p7s
Description: S/MIME Cryptographic Signature