libstdc++: add initializer_list constructor to std::span (P2447R6)

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: Add the new feature-testing macro.
	* include/bits/version.h: Regenerate.
	* include/std/span: Add 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.

Signed-off-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
This commit is contained in:
Giuseppe D'Angelo
2024-12-20 12:09:10 +00:00
committed by Jonathan Wakely
parent cbef2c1dbd
commit 5db0687384
5 changed files with 136 additions and 0 deletions

View File

@@ -1869,6 +1869,14 @@ ftms = {
};
};
ftms = {
name = span_initializer_list;
values = {
v = 202311;
cxxmin = 26;
};
};
ftms = {
name = text_encoding;
values = {

View File

@@ -2075,6 +2075,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

View File

@@ -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
@@ -228,6 +232,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _M_ptr(ranges::data(__range)), _M_extent(ranges::size(__range))
{ }
#if __cpp_lib_span_initializer_list >= 202311L // >= C++26
#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_ptr(__il.begin()), _M_extent(__il.size())
{
}
#pragma GCC diagnostic pop
#endif
constexpr
span(const span&) noexcept = default;

View File

@@ -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 initializer_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 initializer_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);
}

View File

@@ -0,0 +1,36 @@
// { dg-do run { target { c++20 && c++23_down } } }
// { dg-do compile { target c++26 } }
#include <span>
#include <utility>
#include <testsuite_hooks.h>
struct Any {
constexpr Any() { }
template<typename T> constexpr Any(T) { }
};
// 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 Any> v) { return v.size(); }
void *array3[10];
Any array4[10];
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 } }
#if __cpp_lib_span_initializer_list
static_assert(three({array3, 0}) == 2);
static_assert(four({array4, array4 + 10}) == 2);
#else
static_assert(three({array3, 0}) == 0);
static_assert(four({array4, array4 + 10}) == 10);
#endif
}