Commit 4ebba9d0 authored by Jan Wilken Dörrie's avatar Jan Wilken Dörrie Committed by Commit Bot

[base] Allow qualification conversions from std::array in base::span

This change implements the resolution of LWG3255 in base::span.
Following this change base::span allows conversions from std::array that
add const or volatile qualifications.

References:
- https://cplusplus.github.io/LWG/issue3255
- https://github.com/cplusplus/draft/commit/2ae381

Bug: 828324
Change-Id: Ib58e769b841f58778a547109f654cceda3a75cd5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2087720
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747218}
parent f1106d79
......@@ -256,18 +256,18 @@ class span : public internal::ExtentStorage<Extent> {
constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {}
template <
typename U,
size_t N,
typename = internal::
EnableIfSpanCompatibleArray<std::array<value_type, N>&, T, Extent>>
constexpr span(std::array<value_type, N>& array) noexcept
typename =
internal::EnableIfSpanCompatibleArray<std::array<U, N>&, T, Extent>>
constexpr span(std::array<U, N>& array) noexcept
: span(base::data(array), N) {}
template <size_t N,
typename = internal::EnableIfSpanCompatibleArray<
const std::array<value_type, N>&,
T,
Extent>>
constexpr span(const std::array<value_type, N>& array) noexcept
template <typename U,
size_t N,
typename = internal::
EnableIfSpanCompatibleArray<const std::array<U, N>&, T, Extent>>
constexpr span(const std::array<U, N>& array) noexcept
: span(base::data(array), N) {}
// Conversion from a container that has compatible base::data() and integral
......
......@@ -97,6 +97,102 @@ TEST(SpanTest, ConstructFromPointerPair) {
EXPECT_EQ(vector[i], static_span[i]);
}
TEST(SpanTest, AllowedConversionsFromStdArray) {
// In the following assertions we use std::is_convertible_v<From, To>, which
// for non-void types is equivalent to checking whether the following
// expression is well-formed:
//
// T obj = std::declval<From>();
//
// In particular we are checking whether From is implicitly convertible to To,
// which also implies that To is explicitly constructible from From.
static_assert(
std::is_convertible<std::array<int, 3>&, base::span<int>>::value,
"Error: l-value reference to std::array<int> should be convertible to "
"base::span<int> with dynamic extent.");
static_assert(
std::is_convertible<std::array<int, 3>&, base::span<int, 3>>::value,
"Error: l-value reference to std::array<int> should be convertible to "
"base::span<int> with the same static extent.");
static_assert(
std::is_convertible<std::array<int, 3>&, base::span<const int>>::value,
"Error: l-value reference to std::array<int> should be convertible to "
"base::span<const int> with dynamic extent.");
static_assert(
std::is_convertible<std::array<int, 3>&, base::span<const int, 3>>::value,
"Error: l-value reference to std::array<int> should be convertible to "
"base::span<const int> with the same static extent.");
static_assert(std::is_convertible<const std::array<int, 3>&,
base::span<const int>>::value,
"Error: const l-value reference to std::array<int> should be "
"convertible to base::span<const int> with dynamic extent.");
static_assert(
std::is_convertible<const std::array<int, 3>&,
base::span<const int, 3>>::value,
"Error: const l-value reference to std::array<int> should be convertible "
"to base::span<const int> with the same static extent.");
static_assert(std::is_convertible<std::array<const int, 3>&,
base::span<const int>>::value,
"Error: l-value reference to std::array<const int> should be "
"convertible to base::span<const int> with dynamic extent.");
static_assert(
std::is_convertible<std::array<const int, 3>&,
base::span<const int, 3>>::value,
"Error: l-value reference to std::array<const int> should be convertible "
"to base::span<const int> with the same static extent.");
static_assert(
std::is_convertible<const std::array<const int, 3>&,
base::span<const int>>::value,
"Error: const l-value reference to std::array<const int> should be "
"convertible to base::span<const int> with dynamic extent.");
static_assert(
std::is_convertible<const std::array<const int, 3>&,
base::span<const int, 3>>::value,
"Error: const l-value reference to std::array<const int> should be "
"convertible to base::span<const int> with the same static extent.");
}
TEST(SpanTest, DisallowedConstructionsFromStdArray) {
// In the following assertions we use !std::is_constructible_v<T, Args>, which
// is equivalent to checking whether the following expression is malformed:
//
// T obj(std::declval<Args>()...);
//
// In particular we are checking that T is not explicitly constructible from
// Args, which also implies that T is not implicitly constructible from Args
// as well.
static_assert(
!std::is_constructible<base::span<int>, const std::array<int, 3>&>::value,
"Error: base::span<int> with dynamic extent should not be constructible "
"from const l-value reference to std::array<int>");
static_assert(
!std::is_constructible<base::span<int>, std::array<const int, 3>&>::value,
"Error: base::span<int> with dynamic extent should not be constructible "
"from l-value reference to std::array<const int>");
static_assert(
!std::is_constructible<base::span<int>,
const std::array<const int, 3>&>::value,
"Error: base::span<int> with dynamic extent should not be constructible "
"const from l-value reference to std::array<const int>");
static_assert(
!std::is_constructible<base::span<int, 2>, std::array<int, 3>&>::value,
"Error: base::span<int> with static extent should not be constructible "
"from l-value reference to std::array<int> with different extent");
static_assert(
!std::is_constructible<base::span<int, 4>, std::array<int, 3>&>::value,
"Error: base::span<int> with dynamic extent should not be constructible "
"from l-value reference to std::array<int> with different extent");
static_assert(
!std::is_constructible<base::span<int>, std::array<bool, 3>&>::value,
"Error: base::span<int> with dynamic extent should not be constructible "
"from l-value reference to std::array<bool>");
}
TEST(SpanTest, ConstructFromConstexprArray) {
static constexpr int kArray[] = {5, 4, 3, 2, 1};
......
......@@ -37,24 +37,6 @@ void WontCompile() {
span<int, 1> span(array);
}
#elif defined(NCTEST_SPAN_FROM_STD_ARRAY_WITH_NON_MATCHING_STATIC_EXTENT_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span<int, 2>'"]
// A span with static extent constructed from std::array must match the size of
// the array.
void WontCompile() {
std::array<int, 3> array = {1, 2, 3};
span<int, 2> span(array);
}
#elif defined(NCTEST_SPAN_FROM_CONST_STD_ARRAY_WITH_NON_MATCHING_STATIC_EXTENT_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span<const int, 2>'"]
// A span with static extent constructed from std::array must match the size of
// the array.
void WontCompile() {
const std::array<int, 3> array = {1, 2, 3};
span<const int, 2> span(array);
}
#elif defined(NCTEST_SPAN_FROM_OTHER_SPAN_WITH_MISMATCHING_EXTENT_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span<int, 4>'"]
// A span with static extent constructed from another span must match the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment