diff --git a/include/span.h b/include/span.h index 38cf9f5..4983295 100644 --- a/include/span.h +++ b/include/span.h @@ -94,20 +94,22 @@ public: constexpr static const index_type extent = Extent; // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : data_(nullptr), size_(0) - { static_assert(extent == dynamic_extent || extent == 0, "Cannot default initialize a fixed-length span."); } + constexpr span() noexcept : storage_(nullptr, extent_type<0>()) + {} constexpr span(nullptr_t) noexcept : span() {} - constexpr span(pointer ptr, index_type count) : data_(ptr), size_(count) - { Expects(((!ptr && count == 0) || (ptr && count >= 0)) && (extent == dynamic_extent || extent == count)); } + constexpr span(pointer ptr, index_type count) : storage_(ptr, count) + { Expects(((!ptr && count == 0) || (ptr && count >= 0))); } - constexpr span(pointer firstElem, pointer lastElem) : data_(firstElem), size_(std::distance(firstElem, lastElem)) - { Expects(size_ >= 0 && (extent == dynamic_extent || extent == size_)); } + constexpr span(pointer firstElem, pointer lastElem) + : storage_(firstElem, std::distance(firstElem, lastElem)) + {} template - constexpr span(element_type(&arr)[N]) {} + constexpr span(element_type(&arr)[N]) : storage_(&arr[0], extent_type()) + {} #if 0 // TODO template @@ -141,7 +143,7 @@ public: #endif // [span.obs], span observers constexpr index_type length() const noexcept { return size(); } - constexpr index_type size() const noexcept { return size_; } + constexpr index_type size() const noexcept { return storage_.size(); } constexpr index_type length_bytes() const noexcept { return size_bytes(); } constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } constexpr bool empty() const noexcept { return size() == 0; } @@ -149,11 +151,11 @@ public: // [span.elem], span element access constexpr reference operator[](index_type idx) const { - Expects(idx >= 0 && idx < size_); - return data_[idx]; + Expects(idx >= 0 && idx < storage_.size()); + return storage_.data()[idx]; } constexpr reference operator()(index_type idx) const { return this->operator[](idx); } - constexpr pointer data() const noexcept { return data_; } + constexpr pointer data() const noexcept { return storage_.data(); } #if 0 // TODO // [span.iter], span iterator support iterator begin() const noexcept; @@ -169,8 +171,70 @@ public: const_reverse_iterator crend() const noexcept; #endif private: - pointer data_; - index_type size_; + template + class extent_type; + + template + class extent_type + { + public: + static_assert(Extent >= 0, "A fixed-size span must be >= 0 in size."); + + constexpr extent_type() noexcept {} + + template + constexpr extent_type(extent_type) noexcept + { + static_assert(Other == Extent, + "Mismatch between fixed-size extent and size of initializing data."); + } + + constexpr extent_type(index_type size) + { Expects(size == Extent); } + + constexpr inline index_type size() const noexcept { return Extent; } + }; + + template <> + class extent_type + { + public: + template + explicit constexpr extent_type(extent_type ext) : size_(ext.size()) + {} + + explicit constexpr extent_type(index_type size) : size_(size) + { Expects(size >= 0); } + + constexpr inline index_type size() const noexcept + { return size_; } + + private: + index_type size_; + }; + + // this implementation detail class lets us take advantage of the + // empty base class optimization to pay for only storage of a single + // pointer in the case of fixed-size spans + template + class storage_type : public ExtentType + { + public: + template + storage_type(pointer data, OtherExtentType ext) + : ExtentType(ext), data_(data) {} + + //storage_type(pointer data, ExtentType ext) + // : ExtentType(ext), data_(data) {} + + constexpr inline pointer data() const noexcept + { return data_; } + + private: + pointer data_; + }; + + storage_type> storage_; }; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 11004ae..e4bdef4 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -73,6 +73,19 @@ SUITE(span_tests) } } + TEST(size_optimization) + { + { + span s; + CHECK(sizeof(s) == sizeof(int*) + sizeof(ptrdiff_t)); + } + + { + span s; + CHECK(sizeof(s) == sizeof(int*)); + } + } + TEST(from_nullptr_constructor) { { @@ -250,7 +263,7 @@ SUITE(span_tests) // CHECK_THROW(workaround_macro(), fail_fast); //} } -#if 0 + TEST(from_array_constructor) { int arr[5] = {1, 2, 3, 4, 5}; @@ -265,10 +278,11 @@ SUITE(span_tests) CHECK(s.length() == 5 && s.data() == &arr[0]); } - { + int arr2d[2][3] = { 1, 2, 3, 4, 5, 6 }; + #ifdef CONFIRM_COMPILATION_ERRORS + { span s{arr}; -#endif } { @@ -276,8 +290,6 @@ SUITE(span_tests) CHECK(s.length() == 0 && s.data() == &arr[0]); } - int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; - { span s{arr2d}; CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); @@ -290,43 +302,17 @@ SUITE(span_tests) } { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; + span s{ arr2d }; + } #endif - } - { - span s{arr2d}; - CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); - CHECK(s[0] == 1 && s[5] == 6); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; -#endif - } - - { - span s{arr2d[0]}; + span s{ arr2d[0] }; CHECK(s.length() == 1 && s.data() == &arr2d[0]); } - { - span s{arr2d}; - CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); - auto workaround_macro = [&]() { return s[{1, 2}] == 6; }; - CHECK(workaround_macro()); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; -#endif - } - int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; +#ifdef CONFIRM_COMPILATION_ERRORS { span s{arr3d}; CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); @@ -339,9 +325,7 @@ SUITE(span_tests) } { -#ifdef CONFIRM_COMPILATION_ERRORS span s{arr3d}; -#endif } { @@ -349,32 +333,13 @@ SUITE(span_tests) CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); CHECK(s[0] == 1 && s[5] == 6); } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr3d}; #endif - } - { - span s{arr3d[0]}; - CHECK(s.length() == 1 && s.data() == &arr3d[0]); - } - - { - span s{arr3d}; - CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); - auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; }; - CHECK(workaround_macro()); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr3d}; -#endif + //span s{arr3d[0]}; + //CHECK(s.length() == 1 && s.data() == &arr3d[0]); } } - +#if 0 TEST(from_dynamic_array_constructor) { double(*arr)[3][4] = new double[100][3][4];