Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/beman/span/span.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <array>
#include <cassert>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <ranges>
Expand Down Expand Up @@ -140,6 +141,23 @@ class span {
}
}

// initializer_list constructor (P4190R0).
// The same_as match (not is_convertible) is what stops span<const bool>
// from silently grabbing this overload for a (bool*, size_t) argument
// pair - the bug that got the C++26 version (P2447R6) reverted by P4144R1.
// T_ defaults to ElementType so is_const_v stays dependent on a function
// template parameter; otherwise it would hard-error in span<int>.
template <class InitListValueType,
class T_ = ElementType,
std::enable_if_t<std::is_const_v<T_>, int> = 0,
std::enable_if_t<std::is_same_v<InitListValueType, std::remove_cv_t<T_>>, int> = 0>
constexpr explicit(Extent != dynamic_extent) span(std::initializer_list<InitListValueType> il)
: data_(il.begin()), size_(il.size()) {
if constexpr (Extent != dynamic_extent) {
assert(il.size() == Extent);
}
}

// Cross-span converting constructor.
template <class OtherElementType,
std::size_t OtherExtent,
Expand Down
79 changes: 73 additions & 6 deletions tests/beman/span/span.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
#include <algorithm>
#include <array>
#include <cstddef>
#include <initializer_list>
#include <numeric>
#include <type_traits>
#include <utility>
#include <vector>

namespace bsp = beman::span;
Expand Down Expand Up @@ -170,6 +173,70 @@ TEST(SpanConstruction, dynamic_from_static) {
EXPECT_EQ(dynamic.size(), 4u);
}

TEST(SpanInitList, dynamic_from_braced_list) {
auto verify = [](bsp::span<const int> s) {
EXPECT_EQ(s.size(), 3u);
EXPECT_EQ(s[0], 1);
EXPECT_EQ(s[1], 2);
EXPECT_EQ(s[2], 3);
};
verify({1, 2, 3});
}

TEST(SpanInitList, fixed_extent_from_braced_list) {
auto verify = [](bsp::span<const int, 3> s) {
EXPECT_EQ(s.size(), 3u);
EXPECT_EQ(s[0], 1);
EXPECT_EQ(s[2], 3);
};
verify(bsp::span<const int, 3>{1, 2, 3});
}

TEST(SpanInitList, const_bool_from_braced_list) {
auto verify = [](bsp::span<const bool> s) {
EXPECT_EQ(s.size(), 3u);
EXPECT_TRUE(s[0]);
EXPECT_FALSE(s[1]);
EXPECT_TRUE(s[2]);
};
verify({true, false, true});
}

TEST(SpanInitList, named_initializer_list_keeps_array_alive) {
std::initializer_list<int> il = {10, 20, 30};
bsp::span<const int> s(il);
EXPECT_EQ(s.size(), 3u);
EXPECT_EQ(s[0], 10);
EXPECT_EQ(s[2], 30);
}

TEST(SpanInitList, pointer_and_size_resolves_to_pointer_count_ctor) {
bool data[4] = {true, false, true, false};
bool* ptr = data;
std::size_t n = 4;
bsp::span<const bool> s(ptr, n);
EXPECT_EQ(s.size(), 4u);
EXPECT_EQ(s.data(), data);
}

namespace init_list_detail {
template <class T, class IL, class = void>
struct constructible_from_il : std::false_type {};

template <class T, class IL>
struct constructible_from_il<T, IL, std::void_t<decltype(T(std::declval<IL>()))>> : std::true_type {};
} // namespace init_list_detail

TEST(SpanInitList, non_const_element_type_rejected) {
static_assert(init_list_detail::constructible_from_il<bsp::span<const int>, std::initializer_list<int>>::value);
static_assert(!init_list_detail::constructible_from_il<bsp::span<int>, std::initializer_list<int>>::value);
}

TEST(SpanInitList, exact_type_match_required) {
static_assert(!init_list_detail::constructible_from_il<bsp::span<const float>, std::initializer_list<int>>::value);
static_assert(!init_list_detail::constructible_from_il<bsp::span<const int>, std::initializer_list<long>>::value);
}

// ---------------------------------------------------------------------------
// Element access
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -203,29 +270,29 @@ TEST(SpanElementAccess, write_through_span) {

// at() bounds-checked access (P2821R5, C++26)
TEST(SpanAt, in_bounds_returns_element) {
int arr[] = {10, 20, 30};
int arr[] = {10, 20, 30};
bsp::span<int> s(arr);
EXPECT_EQ(s.at(0), 10);
EXPECT_EQ(s.at(1), 20);
EXPECT_EQ(s.at(2), 30);
}

TEST(SpanAt, returns_lvalue_reference_for_mutable_span) {
int arr[] = {1, 2, 3};
int arr[] = {1, 2, 3};
bsp::span<int> s(arr);
s.at(1) = 42;
EXPECT_EQ(arr[1], 42);
static_assert(std::is_same_v<decltype(s.at(0)), int&>);
}

TEST(SpanAt, throws_out_of_range_at_size_boundary) {
int arr[] = {1, 2, 3};
int arr[] = {1, 2, 3};
bsp::span<int> s(arr);
EXPECT_THROW(s.at(3), std::out_of_range);
}

TEST(SpanAt, throws_out_of_range_well_past_size) {
int arr[] = {1, 2, 3};
int arr[] = {1, 2, 3};
bsp::span<int> s(arr);
EXPECT_THROW(s.at(100), std::out_of_range);
}
Expand All @@ -236,14 +303,14 @@ TEST(SpanAt, empty_span_always_throws) {
}

TEST(SpanAt, fixed_extent_in_bounds_and_out_of_range) {
int arr[] = {7, 8, 9, 10};
int arr[] = {7, 8, 9, 10};
bsp::span<int, 4> s(arr);
EXPECT_EQ(s.at(3), 10);
EXPECT_THROW(s.at(4), std::out_of_range);
}

TEST(SpanAt, const_span_returns_reference_to_const) {
const int arr[] = {1, 2, 3};
const int arr[] = {1, 2, 3};
bsp::span<const int> s(arr);
EXPECT_EQ(s.at(2), 3);
static_assert(std::is_same_v<decltype(s.at(0)), const int&>);
Expand Down