diff --git a/include/beman/span/span.hpp b/include/beman/span/span.hpp index 48c7f25..cb4aa42 100644 --- a/include/beman/span/span.hpp +++ b/include/beman/span/span.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -140,6 +141,23 @@ class span { } } + // initializer_list constructor (P4190R0). + // The same_as match (not is_convertible) is what stops span + // 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. + template , int> = 0, + std::enable_if_t>, int> = 0> + constexpr explicit(Extent != dynamic_extent) span(std::initializer_list il) + : data_(il.begin()), size_(il.size()) { + if constexpr (Extent != dynamic_extent) { + assert(il.size() == Extent); + } + } + // Cross-span converting constructor. template #include #include +#include #include +#include +#include #include namespace bsp = beman::span; @@ -170,6 +173,70 @@ TEST(SpanConstruction, dynamic_from_static) { EXPECT_EQ(dynamic.size(), 4u); } +TEST(SpanInitList, dynamic_from_braced_list) { + auto verify = [](bsp::span 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 s) { + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(s[0], 1); + EXPECT_EQ(s[2], 3); + }; + verify(bsp::span{1, 2, 3}); +} + +TEST(SpanInitList, const_bool_from_braced_list) { + auto verify = [](bsp::span 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 il = {10, 20, 30}; + bsp::span 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 s(ptr, n); + EXPECT_EQ(s.size(), 4u); + EXPECT_EQ(s.data(), data); +} + +namespace init_list_detail { +template +struct constructible_from_il : std::false_type {}; + +template +struct constructible_from_il()))>> : std::true_type {}; +} // namespace init_list_detail + +TEST(SpanInitList, non_const_element_type_rejected) { + static_assert(init_list_detail::constructible_from_il, std::initializer_list>::value); + static_assert(!init_list_detail::constructible_from_il, std::initializer_list>::value); +} + +TEST(SpanInitList, exact_type_match_required) { + static_assert(!init_list_detail::constructible_from_il, std::initializer_list>::value); + static_assert(!init_list_detail::constructible_from_il, std::initializer_list>::value); +} + // --------------------------------------------------------------------------- // Element access // --------------------------------------------------------------------------- @@ -203,7 +270,7 @@ 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 s(arr); EXPECT_EQ(s.at(0), 10); EXPECT_EQ(s.at(1), 20); @@ -211,7 +278,7 @@ TEST(SpanAt, in_bounds_returns_element) { } TEST(SpanAt, returns_lvalue_reference_for_mutable_span) { - int arr[] = {1, 2, 3}; + int arr[] = {1, 2, 3}; bsp::span s(arr); s.at(1) = 42; EXPECT_EQ(arr[1], 42); @@ -219,13 +286,13 @@ TEST(SpanAt, returns_lvalue_reference_for_mutable_span) { } TEST(SpanAt, throws_out_of_range_at_size_boundary) { - int arr[] = {1, 2, 3}; + int arr[] = {1, 2, 3}; bsp::span 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 s(arr); EXPECT_THROW(s.at(100), std::out_of_range); } @@ -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 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 s(arr); EXPECT_EQ(s.at(2), 3); static_assert(std::is_same_v);