[libc++][span] P2447R4: std::span over an initializer list (#78157)

Implements: https://wg21.link/P2447R6
- https://eel.is/c++draft/span.syn
- https://eel.is/c++draft/span.overview
- https://eel.is/c++draft/span.cons
- https://eel.is/c++draft/diff

---------

Co-authored-by: Zingam <zingam@outlook.com>
This commit is contained in:
Hristo Hristov 2024-01-20 06:09:46 +02:00 committed by GitHub
parent 552f5549de
commit dbbeee6b83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 238 additions and 46 deletions

View File

@ -438,7 +438,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_span_at`` ``202311L``
--------------------------------------------------- -----------------
``__cpp_lib_span_initializer_list`` *unimplemented*
``__cpp_lib_span_initializer_list`` ``202311L``
--------------------------------------------------- -----------------
``__cpp_lib_sstream_from_string_view`` *unimplemented*
--------------------------------------------------- -----------------

View File

@ -61,6 +61,7 @@ Implemented Papers
- P1759R6 - Native handles and file streams
- P2868R3 - Remove Deprecated ``std::allocator`` Typedef From C++26
- P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``
- P2447R6 - ``span`` over initializer list
Improvements and New Features

View File

@ -34,7 +34,7 @@
"`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|"
"`P2909R4 <https://wg21.link/P2909R4>`__","LWG","Fix formatting of code units as integers (Dude, wheres my ``char``?)","Kona November 2023","|Complete|","18.0","|format| |DR|"
"`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","","",""
"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","|Complete|","18.0",""
"`P2821R5 <https://wg21.link/P2821R5>`__","LWG","``span.at()``","Kona November 2023","|Complete|","18.0",""
"`P2868R3 <https://wg21.link/P2868R3>`__","LWG","Remove Deprecated ``std::allocator`` Typedef From C++26","Kona November 2023","|Complete|","18.0",""
"`P2870R3 <https://wg21.link/P2870R3>`__","LWG","Remove ``basic_string::reserve()`` From C++26","Kona November 2023","|Complete|","18.0",""

1 Paper # Group Paper Name Meeting Status First released version Labels
34 `P2918R2 <https://wg21.link/P2918R2>`__ LWG Runtime format strings II Kona November 2023 |Complete| 18.0 |format|
35 `P2909R4 <https://wg21.link/P2909R4>`__ LWG Fix formatting of code units as integers (Dude, where’s my ``char``?) Kona November 2023 |Complete| 18.0 |format| |DR|
36 `P0952R2 <https://wg21.link/P0952R2>`__ LWG A new specification for ``std::generate_canonical`` Kona November 2023
37 `P2447R6 <https://wg21.link/P2447R6>`__ LWG ``std::span`` over an initializer list Kona November 2023 |Complete| 18.0
38 `P2821R5 <https://wg21.link/P2821R5>`__ LWG ``span.at()`` Kona November 2023 |Complete| 18.0
39 `P2868R3 <https://wg21.link/P2868R3>`__ LWG Remove Deprecated ``std::allocator`` Typedef From C++26 Kona November 2023 |Complete| 18.0
40 `P2870R3 <https://wg21.link/P2870R3>`__ LWG Remove ``basic_string::reserve()`` From C++26 Kona November 2023 |Complete| 18.0

View File

@ -68,6 +68,7 @@ public:
constexpr span(const array<value_type, N>& arr) noexcept;
template<class R>
constexpr explicit(Extent != dynamic_extent) span(R&& r);
constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
constexpr span(const span& other) noexcept = default;
template <class OtherElementType, size_t OtherExtent>
constexpr explicit(Extent != dynamic_extent) span(const span<OtherElementType, OtherExtent>& s) noexcept;
@ -228,6 +229,15 @@ public:
requires(_Sz == 0)
_LIBCPP_HIDE_FROM_ABI constexpr span() noexcept : __data_{nullptr} {}
# if _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI constexpr explicit span(std::initializer_list<value_type> __il)
requires is_const_v<element_type>
: __data_{__il.begin()} {
_LIBCPP_ASSERT_VALID_INPUT_RANGE(
_Extent == __il.size(), "Size mismatch in span's constructor _Extent != __il.size().");
}
# endif
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span&) noexcept = default;
@ -397,6 +407,12 @@ public:
// [span.cons], span constructors, copy, assignment, and destructor
_LIBCPP_HIDE_FROM_ABI constexpr span() noexcept : __data_{nullptr}, __size_{0} {}
# if _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI constexpr span(std::initializer_list<value_type> __il)
requires is_const_v<element_type>
: __data_{__il.begin()}, __size_{__il.size()} {}
# endif
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span&) noexcept = default;

View File

@ -507,7 +507,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
// # define __cpp_lib_saturation_arithmetic 202311L
// # define __cpp_lib_smart_ptr_owner_equality 202306L
# define __cpp_lib_span_at 202311L
// # define __cpp_lib_span_initializer_list 202311L
# define __cpp_lib_span_initializer_list 202311L
// # define __cpp_lib_sstream_from_string_view 202306L
// # define __cpp_lib_submdspan 202306L
// # define __cpp_lib_text_encoding 202306L

View File

@ -94,7 +94,11 @@ constexpr bool testSpan()
assert(s4.data() == val && s4.size() == 2);
std::span<const int> s5 = {{1,2}};
#if TEST_STD_VER >= 26
std::span<const int, 2> s6({1, 2});
#else
std::span<const int, 2> s6 = {{1,2}};
#endif
assert(s5.size() == 2); // and it dangles
assert(s6.size() == 2); // and it dangles

View File

@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
// REQUIRES: has-unix-headers
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
// XFAIL: availability-verbose_abort-missing
// <span>
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
#include <cassert>
#include <initializer_list>
#include <span>
#include "check_assertion.h"
int main(int, char**) {
TEST_LIBCPP_ASSERT_FAILURE(
(std::span<const int, 4>({1, 2, 3, 9084, 5})), "Size mismatch in span's constructor _Extent != __il.size().");
TEST_LIBCPP_ASSERT_FAILURE((std::span<const int, 4>(std::initializer_list<int>{1, 2, 3, 9084, 5})),
"Size mismatch in span's constructor _Extent != __il.size().");
return 0;
}

View File

@ -5,39 +5,134 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <span>
#include <span>
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
#include <any>
#include <cassert>
#include <cstddef>
#include <initializer_list>
#include <span>
#include <type_traits>
#include "test_convertible.h"
#include "test_macros.h"
#if TEST_STD_VER >= 26
// SFINAE
template <typename T>
concept ConstElementType = std::is_const_v<typename T::element_type>;
static_assert(ConstElementType<std::span<const int>>);
static_assert(!ConstElementType<std::span<int>>);
static_assert(ConstElementType<std::span<const int, 94>>);
static_assert(!ConstElementType<std::span<int, 94>>);
// Constructor constraings
template <typename I, typename T, std::size_t... N>
concept HasInitializerListCtr = requires(I il) { std::span<T, N...>{il}; };
static_assert(HasInitializerListCtr<std::initializer_list<const int>, const int>);
static_assert(!HasInitializerListCtr<std::initializer_list<int>, int>);
static_assert(HasInitializerListCtr<std::initializer_list<const int>, const int, 94>);
static_assert(!HasInitializerListCtr<std::initializer_list<int>, int, 94>);
// Constructor conditionally explicit
static_assert(!test_convertible<std::span<const int, 28>, std::initializer_list<int>>(),
"This constructor must be explicit");
static_assert(std::is_constructible_v<std::span<const int, 28>, std::initializer_list<int>>);
static_assert(test_convertible<std::span<const int>, std::initializer_list<int>>(),
"This constructor must not be explicit");
static_assert(std::is_constructible_v<std::span<const int>, std::initializer_list<int>>);
#endif
struct Sink {
constexpr Sink() = default;
constexpr Sink(Sink*) {}
constexpr Sink() = default;
constexpr Sink(Sink*) {}
};
constexpr std::size_t count(std::span<const Sink> sp) {
return sp.size();
}
constexpr std::size_t count(std::span<const Sink> sp) { return sp.size(); }
template<int N>
constexpr std::size_t countn(std::span<const Sink, N> sp) {
return sp.size();
template <std::size_t N>
constexpr std::size_t count_n(std::span<const Sink, N> sp) {
return sp.size();
}
constexpr bool test() {
#if TEST_STD_VER >= 26
// Dynamic extent
{
Sink a[10];
assert(count({a}) == 1);
assert(count({a, a + 10}) == 2);
assert(count({a, a + 1, a + 2}) == 3);
assert(count(std::initializer_list<Sink>{a[0], a[1], a[2], a[3]}) == 4);
}
#else
{
Sink a[10];
assert(count({a}) == 10);
assert(count({a, a+10}) == 10);
assert(countn<10>({a}) == 10);
return true;
assert(count({a, a + 10}) == 10);
assert(count_n<10>({a}) == 10);
}
#endif
return true;
}
// Test P2447R4 "Annex C examples"
constexpr int three(std::span<void* const> sp) { return sp.size(); }
constexpr int four(std::span<const std::any> sp) { return sp.size(); }
bool test_P2447R4_annex_c_examples() {
// 1. Overload resolution is affected
// --> tested in "initializer_list.verify.cpp"
// 2. The `initializer_list` ctor has high precedence
// --> tested in "initializer_list.verify.cpp"
// 3. Implicit two-argument construction with a highly convertible value_type
#if TEST_STD_VER >= 26
{
void* a[10];
assert(three({a, 0}) == 2);
}
{
std::any a[10];
assert(four({a, a + 10}) == 2);
}
#else
{
void* a[10];
assert(three({a, 0}) == 0);
}
{
std::any a[10];
assert(four({a, a + 10}) == 10);
}
#endif
return true;
}
int main(int, char**) {
test();
static_assert(test());
assert(test());
static_assert(test());
return 0;
assert(test_P2447R4_annex_c_examples());
return 0;
}

View File

@ -0,0 +1,48 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// <span>
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
#include <span>
#include <utility>
#include "test_macros.h"
// Test P2447R4 "Annex C examples"
void one(std::pair<int, int>);
void one(std::span<const int>);
void two(std::span<const int, 2>);
void test_P2447R4_annex_c_examples() {
// 1. Overload resolution is affected
#if TEST_STD_VER >= 26
// expected-error@+1 {{call to 'one' is ambiguous}}
one({1, 2});
#else
// expected-no-diagnostics
one({1, 2});
#endif
// 2. The `initializer_list` ctor has high precedence
#if TEST_STD_VER >= 26
// expected-error@+1 {{chosen constructor is explicit in copy-initialization}}
two({{1, 2}});
#else
// expected-no-diagnostics
two({{1, 2}});
#endif
// 3. Implicit two-argument construction with a highly convertible value_type
// --> tested in "initializer_list.pass.cpp"
}

View File

@ -13,22 +13,31 @@
// constexpr explicit(Extent != dynamic_extent) span(It first, size_type count);
// If Extent is not equal to dynamic_extent, then count shall be equal to Extent.
//
// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il); // Since C++26
#include <span>
#include <cstddef>
#include "test_macros.h"
template <class T, std::size_t extent>
std::span<T, extent> createImplicitSpan(T* ptr, std::size_t len) {
return {ptr, len}; // expected-error {{chosen constructor is explicit in copy-initialization}}
}
void f() {
void test() {
// explicit constructor necessary
int arr[] = {1, 2, 3};
createImplicitSpan<int, 1>(arr, 3);
std::span<int> sp = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<int>'}}
std::span<int, 2> sp2 = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<int, 2>'}}
std::span<const int> csp = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<const int>'}}
std::span<const int, 2> csp2 = {0, 0}; // expected-error {{no matching constructor for initialization of 'std::span<const int, 2>'}}
// expected-error@+1 {{no matching constructor for initialization of 'std::span<int>'}}
std::span<int> sp = {0, 0};
// expected-error@+1 {{no matching constructor for initialization of 'std::span<int, 2>'}}
std::span<int, 2> sp2 = {0, 0};
#if TEST_STD_VER < 26
// expected-error@+1 {{no matching constructor for initialization of 'std::span<const int>'}}
std::span<const int> csp = {0, 0};
// expected-error@+1 {{no matching constructor for initialization of 'std::span<const int, 2>'}}
std::span<const int, 2> csp2 = {0, 0};
#endif
}

View File

@ -116,17 +116,11 @@
# error "__cpp_lib_span_at should have the value 202311L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_span_initializer_list
# error "__cpp_lib_span_initializer_list should be defined in c++26"
# endif
# if __cpp_lib_span_initializer_list != 202311L
# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_span_initializer_list
# error "__cpp_lib_span_initializer_list should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_span_initializer_list
# error "__cpp_lib_span_initializer_list should be defined in c++26"
# endif
# if __cpp_lib_span_initializer_list != 202311L
# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -7294,17 +7294,11 @@
# error "__cpp_lib_span_at should have the value 202311L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_span_initializer_list
# error "__cpp_lib_span_initializer_list should be defined in c++26"
# endif
# if __cpp_lib_span_initializer_list != 202311L
# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_span_initializer_list
# error "__cpp_lib_span_initializer_list should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_span_initializer_list
# error "__cpp_lib_span_initializer_list should be defined in c++26"
# endif
# if __cpp_lib_span_initializer_list != 202311L
# error "__cpp_lib_span_initializer_list should have the value 202311L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)

View File

@ -1093,7 +1093,6 @@ feature_test_macros = [
"name": "__cpp_lib_span_initializer_list",
"values": {"c++26": 202311}, # P2447R6 std::span over an initializer list
"headers": ["span"],
"unimplemented": True,
},
{
"name": "__cpp_lib_spanstream",