[libcxx][span] Implement P1976R2

This resolves the NB comment about the construction of a fixed-size span
from a dynamic range.

Differential Revision: https://reviews.llvm.org/D74577
This commit is contained in:
Michael Schellenberger Costa 2020-05-13 09:50:06 -04:00 committed by Louis Dionne
parent 2668775f66
commit 6d2599e4f7
12 changed files with 206 additions and 38 deletions

View File

@ -198,6 +198,8 @@ Status
------------------------------------------------- -----------------
``__cpp_lib_ranges`` *unimplemented*
------------------------------------------------- -----------------
``__cpp_lib_span`` ``202002L``
------------------------------------------------- -----------------
``__cpp_lib_three_way_comparison`` *unimplemented*
------------------------------------------------- -----------------
``__cpp_lib_to_array`` ``201907L``

View File

@ -53,8 +53,8 @@ public:
// [span.cons], span constructors, copy, assignment, and destructor
constexpr span() noexcept;
constexpr span(pointer ptr, size_type count);
constexpr span(pointer firstElem, pointer lastElem);
constexpr explicit(Extent != dynamic_extent) span(pointer ptr, size_type count);
constexpr explicit(Extent != dynamic_extent) span(pointer firstElem, pointer lastElem);
template <size_t N>
constexpr span(element_type (&arr)[N]) noexcept;
template <size_t N>
@ -62,12 +62,12 @@ public:
template <size_t N>
constexpr span(const array<value_type, N>& arr) noexcept;
template <class Container>
constexpr span(Container& cont);
constexpr explicit(Extent != dynamic_extent) span(Container& cont);
template <class Container>
constexpr span(const Container& cont);
constexpr explicit(Extent != dynamic_extent) span(const Container& cont);
constexpr span(const span& other) noexcept = default;
template <class OtherElementType, size_t OtherExtent>
constexpr span(const span<OtherElementType, OtherExtent>& s) noexcept;
constexpr explicit(Extent != dynamic_extent) span(const span<OtherElementType, OtherExtent>& s) noexcept;
~span() noexcept = default;
constexpr span& operator=(const span& other) noexcept = default;
@ -214,15 +214,31 @@ public:
constexpr span (const span&) noexcept = default;
constexpr span& operator=(const span&) noexcept = default;
_LIBCPP_INLINE_VISIBILITY constexpr span(pointer __ptr, size_type __count) : __data{__ptr}
_LIBCPP_INLINE_VISIBILITY constexpr explicit span(pointer __ptr, size_type __count) : __data{__ptr}
{ (void)__count; _LIBCPP_ASSERT(_Extent == __count, "size mismatch in span's constructor (ptr, len)"); }
_LIBCPP_INLINE_VISIBILITY constexpr span(pointer __f, pointer __l) : __data{__f}
_LIBCPP_INLINE_VISIBILITY constexpr explicit span(pointer __f, pointer __l) : __data{__f}
{ (void)__l; _LIBCPP_ASSERT(_Extent == distance(__f, __l), "size mismatch in span's constructor (ptr, ptr)"); }
_LIBCPP_INLINE_VISIBILITY constexpr span(element_type (&__arr)[_Extent]) noexcept : __data{__arr} {}
_LIBCPP_INLINE_VISIBILITY constexpr span( array<value_type, _Extent>& __arr) noexcept : __data{__arr.data()} {}
_LIBCPP_INLINE_VISIBILITY constexpr span(const array<value_type, _Extent>& __arr) noexcept : __data{__arr.data()} {}
template <class _Container>
_LIBCPP_INLINE_VISIBILITY
constexpr explicit span( _Container& __c,
enable_if_t<__is_span_compatible_container<_Container, _Tp>::value, nullptr_t> = nullptr)
: __data{_VSTD::data(__c)} {
_LIBCPP_ASSERT(_Extent == _VSTD::size(__c), "size mismatch in span's constructor (range)");
}
template <class _Container>
_LIBCPP_INLINE_VISIBILITY
constexpr explicit span(const _Container& __c,
enable_if_t<__is_span_compatible_container<const _Container, _Tp>::value, nullptr_t> = nullptr)
: __data{_VSTD::data(__c)} {
_LIBCPP_ASSERT(_Extent == _VSTD::size(__c), "size mismatch in span's constructor (range)");
}
template <class _OtherElementType>
_LIBCPP_INLINE_VISIBILITY
constexpr span(const span<_OtherElementType, _Extent>& __other,
@ -233,7 +249,7 @@ public:
template <class _OtherElementType>
_LIBCPP_INLINE_VISIBILITY
constexpr span(const span<_OtherElementType, dynamic_extent>& __other,
constexpr explicit span(const span<_OtherElementType, dynamic_extent>& __other,
enable_if_t<
is_convertible_v<_OtherElementType(*)[], element_type (*)[]>,
nullptr_t> = nullptr) noexcept
@ -247,7 +263,7 @@ public:
constexpr span<element_type, _Count> first() const noexcept
{
static_assert(_Count <= _Extent, "Count out of range in span::first()");
return {data(), _Count};
return span<element_type, _Count>{data(), _Count};
}
template <size_t _Count>
@ -255,7 +271,7 @@ public:
constexpr span<element_type, _Count> last() const noexcept
{
static_assert(_Count <= _Extent, "Count out of range in span::last()");
return {data() + size() - _Count, _Count};
return span<element_type, _Count>{data() + size() - _Count, _Count};
}
_LIBCPP_INLINE_VISIBILITY
@ -279,7 +295,9 @@ public:
{
static_assert(_Offset <= _Extent, "Offset out of range in span::subspan()");
static_assert(_Count == dynamic_extent || _Count <= _Extent - _Offset, "Offset + count out of range in span::subspan()");
return {data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
using _ReturnType = span<element_type, _Count != dynamic_extent ? _Count : _Extent - _Offset>;
return _ReturnType{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
}
@ -337,10 +355,10 @@ public:
}
_LIBCPP_INLINE_VISIBILITY span<const byte, _Extent * sizeof(element_type)> __as_bytes() const noexcept
{ return {reinterpret_cast<const byte *>(data()), size_bytes()}; }
{ return span<const byte, _Extent * sizeof(element_type)>{reinterpret_cast<const byte *>(data()), size_bytes()}; }
_LIBCPP_INLINE_VISIBILITY span<byte, _Extent * sizeof(element_type)> __as_writable_bytes() const noexcept
{ return {reinterpret_cast<byte *>(data()), size_bytes()}; }
{ return span<byte, _Extent * sizeof(element_type)>{reinterpret_cast<byte *>(data()), size_bytes()}; }
private:
pointer __data;
@ -418,7 +436,7 @@ public:
constexpr span<element_type, _Count> first() const noexcept
{
_LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::first()");
return {data(), _Count};
return span<element_type, _Count>{data(), _Count};
}
template <size_t _Count>
@ -426,7 +444,7 @@ public:
constexpr span<element_type, _Count> last() const noexcept
{
_LIBCPP_ASSERT(_Count <= size(), "Count out of range in span::last()");
return {data() + size() - _Count, _Count};
return span<element_type, _Count>{data() + size() - _Count, _Count};
}
_LIBCPP_INLINE_VISIBILITY
@ -449,7 +467,7 @@ public:
{
_LIBCPP_ASSERT(_Offset <= size(), "Offset out of range in span::subspan()");
_LIBCPP_ASSERT(_Count == dynamic_extent || _Count <= size() - _Offset, "Offset + count out of range in span::subspan()");
return {data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
return span<element_type, _Count>{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
}
constexpr span<element_type, dynamic_extent>

View File

@ -98,6 +98,7 @@ __cpp_lib_shared_mutex 201505L <shared_mutex>
__cpp_lib_shared_ptr_arrays 201611L <memory>
__cpp_lib_shared_ptr_weak_type 201606L <memory>
__cpp_lib_shared_timed_mutex 201402L <shared_mutex>
__cpp_lib_span 202002L <span>
__cpp_lib_string_udls 201304L <string>
__cpp_lib_string_view 201606L <string> <string_view>
__cpp_lib_three_way_comparison 201711L <compare>
@ -234,8 +235,9 @@ __cpp_lib_void_t 201411L <type_traits>
# endif
# define __cpp_lib_list_remove_return_type 201806L
// # define __cpp_lib_ranges 201811L
# define __cpp_lib_to_array 201907L
# define __cpp_lib_span 202002L
// # define __cpp_lib_three_way_comparison 201711L
# define __cpp_lib_to_array 201907L
#endif
#endif // _LIBCPP_VERSIONH

View File

@ -185,13 +185,14 @@ int main(int, char**)
// constexpr statically sized assignment
{
constexpr std::span<const int,2> spans[] = {
{carr1, 2},
{carr1 + 1, 2},
{carr1 + 2, 2},
{carr2, 2},
{carr2 + 1, 2},
{carr3, 2}
using spanType = std::span<const int,2>;
constexpr spanType spans[] = {
spanType{carr1, 2},
spanType{carr1 + 1, 2},
spanType{carr1 + 2, 2},
spanType{carr2, 2},
spanType{carr2 + 1, 2},
spanType{carr3, 2}
};
static_assert(std::size(spans) == 6, "" );
@ -247,10 +248,11 @@ int main(int, char**)
// statically sized assignment
{
std::span<int,2> spans[] = {
{arr, arr + 2},
{arr + 1, arr + 3},
{arr + 2, arr + 4}
using spanType = std::span<int,2>;
spanType spans[] = {
spanType{arr, arr + 2},
spanType{arr + 1, arr + 3},
spanType{arr + 2, arr + 4}
};
for (size_t i = 0; i < std::size(spans); ++i)
@ -279,10 +281,11 @@ int main(int, char**)
}
{
std::span<std::string, 1> spans[] = {
{strs, strs + 1},
{strs + 1, strs + 2},
{strs + 2, strs + 3}
using spanType = std::span<std::string, 1>;
spanType spans[] = {
spanType{strs, strs + 1},
spanType{strs + 1, strs + 2},
spanType{strs + 2, strs + 3}
};
for (size_t i = 0; i < std::size(spans); ++i)

View File

@ -63,6 +63,10 @@ private:
const T *data() const {return nullptr;}
};
template<class T, size_t extent, class container>
std::span<T, extent> createImplicitSpan(container c) {
return {c}; // expected-error {{chosen constructor is explicit in copy-initialization}}
}
int main(int, char**)
{
@ -106,12 +110,14 @@ int main(int, char**)
std::span< volatile int> s7{cv}; // expected-error {{no matching constructor for initialization of 'std::span<volatile int>'}}
}
// statically sized
// explicit constructor necessary
{
IsAContainer<int> c;
std::span<int,1> s1{c}; // expected-error {{no matching constructor for initialization of 'std::span<int, 1>'}}
}
IsAContainer<int> c;
const IsAContainer<int> cc;
createImplicitSpan<int, 1>(c);
createImplicitSpan<int, 1>(cc);
}
return 0;
}

View File

@ -84,14 +84,32 @@ constexpr bool testConstexprSpan()
return s1.data() == val.getV() && s1.size() == 1;
}
template <typename T>
constexpr bool testConstexprSpanStatic()
{
constexpr IsAContainer<const T> val{};
std::span<const T, 1> s1{val};
return s1.data() == val.getV() && s1.size() == 1;
}
template <typename T>
void testRuntimeSpan()
{
IsAContainer<T> val{};
const IsAContainer<T> cVal;
std::span<T> s1{val};
std::span<const T> s2{cVal};
std::span<T> s1{val};
std::span<const T> s2{cVal};
assert(s1.data() == val.getV() && s1.size() == 1);
assert(s2.data() == cVal.getV() && s2.size() == 1);
}
template <typename T>
void testRuntimeSpanStatic()
{
IsAContainer<T> val{};
const IsAContainer<T> cVal;
std::span<T, 1> s1{val};
std::span<const T, 1> s2{cVal};
assert(s1.data() == val.getV() && s1.size() == 1);
assert(s2.data() == cVal.getV() && s2.size() == 1);
}
@ -105,12 +123,23 @@ int main(int, char**)
static_assert(testConstexprSpan<double>(), "");
static_assert(testConstexprSpan<A>(), "");
static_assert(testConstexprSpanStatic<int>(), "");
static_assert(testConstexprSpanStatic<long>(), "");
static_assert(testConstexprSpanStatic<double>(), "");
static_assert(testConstexprSpanStatic<A>(), "");
testRuntimeSpan<int>();
testRuntimeSpan<long>();
testRuntimeSpan<double>();
testRuntimeSpan<std::string>();
testRuntimeSpan<A>();
testRuntimeSpanStatic<int>();
testRuntimeSpanStatic<long>();
testRuntimeSpanStatic<double>();
testRuntimeSpanStatic<std::string>();
testRuntimeSpanStatic<A>();
checkCV();
return 0;

View File

@ -27,6 +27,11 @@ const int carr[] = {4,5,6};
volatile int varr[] = {7,8,9};
const volatile int cvarr[] = {1,3,5};
template<class T, size_t extent>
std::span<T, extent> createImplicitSpan(T* ptr, size_t len) {
return {ptr, len}; // expected-error {{chosen constructor is explicit in copy-initialization}}
}
int main(int, char**)
{
// We can't check that the size doesn't match - because that's a runtime property
@ -60,5 +65,10 @@ int main(int, char**)
std::span< volatile int,3> s7{cvarr, 3}; // expected-error {{no matching constructor for initialization of 'std::span<volatile int, 3>'}}
}
// explicit constructor necessary
{
createImplicitSpan<int, 1>(arr, 1);
}
return 0;
}

View File

@ -27,6 +27,11 @@ const int carr[] = {4,5,6};
volatile int varr[] = {7,8,9};
const volatile int cvarr[] = {1,3,5};
template<class T, size_t extent>
std::span<T, extent> createImplicitSpan(T* first, T* last) {
return {first, last}; // expected-error {{chosen constructor is explicit in copy-initialization}}
}
int main(int, char**)
{
// We can't check that the size doesn't match - because that's a runtime property
@ -60,5 +65,10 @@ int main(int, char**)
std::span< volatile int,3> s7{cvarr, cvarr + 3}; // expected-error {{no matching constructor for initialization of 'std::span<volatile int, 3>'}}
}
// explicit constructor necessary
{
createImplicitSpan<int, 1>(arr, arr + 1);
}
return 0;
}

View File

@ -24,6 +24,11 @@
#include "test_macros.h"
template<class T, size_t extent, size_t otherExtent>
std::span<T, extent> createImplicitSpan(std::span<T, otherExtent> s) {
return {s}; // expected-error {{chosen constructor is explicit in copy-initialization}}
}
void checkCV ()
{
// std::span< int> sp;
@ -101,5 +106,10 @@ int main(int, char**)
checkCV();
// explicit constructor necessary
{
createImplicitSpan<int, 1>(sp);
}
return 0;
}

View File

@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// WARNING: This test was generated by generate_feature_test_macro_components.py
// and should not be edited manually.
// <span>
// Test the feature test macros defined by <span>
/* Constant Value
__cpp_lib_span 202002L [C++2a]
*/
#include <span>
#include "test_macros.h"
#if TEST_STD_VER < 14
# ifdef __cpp_lib_span
# error "__cpp_lib_span should not be defined before c++2a"
# endif
#elif TEST_STD_VER == 14
# ifdef __cpp_lib_span
# error "__cpp_lib_span should not be defined before c++2a"
# endif
#elif TEST_STD_VER == 17
# ifdef __cpp_lib_span
# error "__cpp_lib_span should not be defined before c++2a"
# endif
#elif TEST_STD_VER > 17
# ifndef __cpp_lib_span
# error "__cpp_lib_span should be defined in c++2a"
# endif
# if __cpp_lib_span != 202002L
# error "__cpp_lib_span should have the value 202002L in c++2a"
# endif
#endif // TEST_STD_VER > 17
int main(int, char**) { return 0; }

View File

@ -85,6 +85,7 @@
__cpp_lib_shared_ptr_arrays 201611L [C++17]
__cpp_lib_shared_ptr_weak_type 201606L [C++17]
__cpp_lib_shared_timed_mutex 201402L [C++14]
__cpp_lib_span 202002L [C++2a]
__cpp_lib_string_udls 201304L [C++14]
__cpp_lib_string_view 201606L [C++17]
__cpp_lib_three_way_comparison 201711L [C++2a]
@ -391,6 +392,10 @@
# error "__cpp_lib_shared_timed_mutex should not be defined before c++14"
# endif
# ifdef __cpp_lib_span
# error "__cpp_lib_span should not be defined before c++2a"
# endif
# ifdef __cpp_lib_string_udls
# error "__cpp_lib_string_udls should not be defined before c++14"
# endif
@ -784,6 +789,10 @@
# endif
# endif
# ifdef __cpp_lib_span
# error "__cpp_lib_span should not be defined before c++2a"
# endif
# ifndef __cpp_lib_string_udls
# error "__cpp_lib_string_udls should be defined in c++14"
# endif
@ -1381,6 +1390,10 @@
# endif
# endif
# ifdef __cpp_lib_span
# error "__cpp_lib_span should not be defined before c++2a"
# endif
# ifndef __cpp_lib_string_udls
# error "__cpp_lib_string_udls should be defined in c++17"
# endif
@ -2116,6 +2129,13 @@
# endif
# endif
# ifndef __cpp_lib_span
# error "__cpp_lib_span should be defined in c++2a"
# endif
# if __cpp_lib_span != 202002L
# error "__cpp_lib_span should have the value 202002L in c++2a"
# endif
# ifndef __cpp_lib_string_udls
# error "__cpp_lib_string_udls should be defined in c++2a"
# endif

View File

@ -591,6 +591,12 @@ feature_test_macros = sorted([ add_version_header(x) for x in [
},
"headers": ["array"],
},
{"name": "__cpp_lib_span",
"values": {
"c++2a": int(202002),
},
"headers": ["span"],
},
]], key=lambda tc: tc["name"])
def get_std_dialects():