From 7e9ac2f86864be7298320185fc3c7f487d1537b0 Mon Sep 17 00:00:00 2001 From: Alfredo Correa Date: Wed, 1 Jan 2025 23:29:58 -0800 Subject: [PATCH 1/4] introduce continuous_layout --- include/boost/multi/array_ref.hpp | 38 ++++++++++++------ include/boost/multi/detail/layout.hpp | 55 +++++++++++++++++++++++++++ include/boost/multi/detail/types.hpp | 5 ++- test/array_ref.cpp | 18 ++++++++- test/element_access.cpp | 2 +- 5 files changed, 103 insertions(+), 15 deletions(-) diff --git a/include/boost/multi/array_ref.hpp b/include/boost/multi/array_ref.hpp index 54c8f4d96..7480c9bf8 100644 --- a/include/boost/multi/array_ref.hpp +++ b/include/boost/multi/array_ref.hpp @@ -87,10 +87,10 @@ namespace boost::multi { template> struct const_subarray; -template::difference_type>> +template::difference_type>> class subarray; -template::difference_type>> +template::difference_type>> class move_subarray; template @@ -3278,14 +3278,30 @@ constexpr auto static_array_cast(Array&& self, Args&&... args) -> decltype(auto) } template -struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2, void>? -: subarray +class array_ref // TODO(correaa) : inheredit from multi::partially_ordered2, void>? +: public subarray< + T, D, ElementPtr, + std::conditional_t<(D == 1), + // continuous_layout<1, typename std::pointer_traits::difference_type>, + layout_t::difference_type>, + layout_t::difference_type> + > +> { - ~array_ref() = default; // lints(cppcoreguidelines-special-member-functions) + using subarray_base = subarray< + T, D, ElementPtr, + std::conditional_t<(D == 1), + // continuous_layout<1, typename std::pointer_traits::difference_type>, + layout_t::difference_type>, + layout_t::difference_type> + > + >; - using layout_type = typename array_ref::types::layout_t; + public: + ~array_ref() = default; // lints(cppcoreguidelines-special-member-functions) - using iterator = typename subarray::iterator; + using layout_type = typename subarray_base::layout_t; + using iterator = typename subarray_base::iterator; public: constexpr // attempt for MSVC @@ -3320,16 +3336,16 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2{}>, decltype(multi::detail::implicit_cast(std::declval()))* = nullptr> // cppcheck-suppress noExplicitConstructor ; to allow terse syntax constexpr /*implicit*/ array_ref(array_ref&& other) // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) - : subarray{other.layout(), ElementPtr{std::move(other).base()}} {} + : subarray_base{other.layout(), ElementPtr{std::move(other).base()}} {} constexpr array_ref(ElementPtr dat, ::boost::multi::extensions_t const& xs) /*noexcept*/ // TODO(correa) eliminate this ctor - : subarray{typename subarray::types::layout_t(xs), dat} {} + : subarray_base{typename subarray::types::layout_t(xs), dat} {} // constexpr array_ref(typename array_ref::extensions_type extensions, typename array_ref::element_ptr dat) noexcept // : subarray{typename array_ref::types::layout_t{extensions}, dat} {} constexpr array_ref(::boost::multi::extensions_t exts, ElementPtr dat) noexcept - : subarray{typename array_ref::types::layout_t(exts), dat} {} + : subarray_base{typename array_ref::types::layout_t(exts), dat} {} template< class Array, @@ -3373,7 +3389,7 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2 il) : array_ref(il.begin(), typename array_ref::extensions_type{static_cast(il.size())}) {} - using subarray::operator=; + using subarray_base::operator=; private: template constexpr auto copy_elements_(It first) { diff --git a/include/boost/multi/detail/layout.hpp b/include/boost/multi/detail/layout.hpp index 5aa94da0e..3901d8151 100644 --- a/include/boost/multi/detail/layout.hpp +++ b/include/boost/multi/detail/layout.hpp @@ -503,6 +503,61 @@ struct monostate : equality_comparable { friend BOOST_MULTI_HD constexpr auto operator==(monostate const& /*self*/, monostate const& /*other*/) {return true;} }; +template +class continuous_layout; + +template +class continuous_layout<1, SSize> { + public: + using dimensionality_type = multi::dimensionality_t; + using rank = std::integral_constant; + static constexpr dimensionality_type rank_v = rank::value; + static constexpr dimensionality_type dimensionality = rank_v; // TODO(correaa) : consider deprecation + + using difference_type = SSize; + using size_type = difference_type; + + using index = difference_type; + using index_range = multi::range; + using index_extension = multi::extension_t; + using extension_type = index_extension; + + auto extension() const -> extension_type; + + using extensions_type = multi::index_extensions<1>; + auto extensions() const -> extensions_type = delete; + + auto is_empty() const -> bool = delete; + auto empty() const -> bool = delete; + + auto is_compact() const = delete; + + auto sub() const -> layout_t<0, SSize> = delete; + + using stride_type = void; // perhaps std::integral_constant + auto stride() const -> stride_type; + + using num_elements_type = void; + auto num_elements() const -> num_elements_type; + + using offset_type = void; + auto offset() const -> offset_type; + + using offsets_type = void; + auto offsets() const -> offsets_type; + + using strides_type = void; // perhaps std::integral_constant + auto strides() const -> stride_type; + + auto size() const -> size_type; + + using nelems_type = void; // perhaps std::integral_constant + auto nelems() const -> nelems_type; + + using sizes_type = void; + auto sizes() const -> sizes_type = delete; +}; + template struct layout_t<0, SSize> : multi::equality_comparable > diff --git a/include/boost/multi/detail/types.hpp b/include/boost/multi/detail/types.hpp index 952a38bad..5a71bb454 100644 --- a/include/boost/multi/detail/types.hpp +++ b/include/boost/multi/detail/types.hpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Alfredo A. Correa +// Copyright 2018-2025 Alfredo A. Correa // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -16,7 +16,8 @@ using size_type = std::make_signed_t; using index = std::make_signed_t; using difference_type = std::make_signed_t; -using dimensionality_type = index; +using dimensionality_t = index; +using dimensionality_type = dimensionality_t; } // end namespace boost::multi #endif // BOOST_MULTI_DETAIL_TYPES_HPP diff --git a/test/array_ref.cpp b/test/array_ref.cpp index 96c5c85f7..4b9282d11 100644 --- a/test/array_ref.cpp +++ b/test/array_ref.cpp @@ -136,6 +136,22 @@ inline auto trace_separate_sub(multi::subarray const& arr) -> int { } // end unnamed namespace auto main() -> int { // NOLINT(readability-function-cognitive-complexity,bugprone-exception-escape) + +{ + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) for test + int icarr[5] = {}; + multi::array_ref const iarrr(std::data(icarr), 5); + static_assert( std::is_base_of_v< + std::random_access_iterator_tag, + std::iterator_traits::iterator>::iterator_category + >); + + // static_assert( std::is_base_of_v< + // std::contiguous_iterator_tag, + // std::iterator_traits::iterator>::iterator_category + // >); +} + BOOST_AUTO_TEST_CASE(array_ref_from_carray) { #if defined(__clang__) #pragma clang diagnostic push @@ -143,7 +159,7 @@ BOOST_AUTO_TEST_CASE(array_ref_from_carray) { #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" #endif - // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): test + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) for test int arr[4][5] = { { 0, 10, 20, 30, 40}, { 50, 60, 70, 80, 90}, diff --git a/test/element_access.cpp b/test/element_access.cpp index 651cf533a..c70a2823f 100644 --- a/test/element_access.cpp +++ b/test/element_access.cpp @@ -148,7 +148,7 @@ auto main() -> int { // NOLINT(readability-function-cognitive-complexity,bugpro BOOST_TEST( end1 == beg1 ); for(; end1 != end2; ++end1) { // NOLINT(altera-id-dependent-backward-branch,altera-unroll-loops) - } + } BOOST_TEST( end1 == end2 ); } From 955b29342a8ecb465ebe15e07865eee9637d9ede Mon Sep 17 00:00:00 2001 From: Alfredo Correa Date: Thu, 2 Jan 2025 14:04:24 -0800 Subject: [PATCH 2/4] layout compact improvements --- include/boost/multi/array.hpp | 4 +- include/boost/multi/array_ref.hpp | 23 ++++---- include/boost/multi/detail/layout.hpp | 68 +++++++++++++++++------- include/boost/multi/detail/tuple_zip.hpp | 3 +- 4 files changed, 63 insertions(+), 35 deletions(-) diff --git a/include/boost/multi/array.hpp b/include/boost/multi/array.hpp index 742f90e3e..dd4403833 100644 --- a/include/boost/multi/array.hpp +++ b/include/boost/multi/array.hpp @@ -228,9 +228,9 @@ struct static_array // NOLINT(fuchsia-multiple-inheritance) : multiple inherita template>::difference_type> constexpr explicit static_array(It first, It last, allocator_type const& alloc) - : array_alloc{alloc}, ref{ + : array_alloc{alloc}, ref( array_alloc::allocate(static_cast::size_type>(layout_type{index_extension(adl_distance(first, last)) * multi::extensions(*first)}.num_elements())), - index_extension(adl_distance(first, last)) * multi::extensions(*first)} { + index_extension(adl_distance(first, last)) * multi::extensions(*first)) { #if defined(__clang__) && defined(__CUDACC__) // TODO(correaa) add workaround for non-default constructible type and use adl_alloc_uninitialized_default_construct_n if constexpr(! std::is_trivially_default_constructible_v && ! multi::force_element_trivial_default_construction ) { diff --git a/include/boost/multi/array_ref.hpp b/include/boost/multi/array_ref.hpp index 7480c9bf8..f1e9b9e09 100644 --- a/include/boost/multi/array_ref.hpp +++ b/include/boost/multi/array_ref.hpp @@ -2698,7 +2698,8 @@ struct const_subarray // NOLINT(fuchsia-multiple-inhe public: constexpr auto broadcasted() const& { - multi::layout_t<2> const new_layout{this->layout(), 0, 0, (std::numeric_limits::max)()}; + multi::layout_t<1> const self_layout{this->layout()}; + multi::layout_t<2> const new_layout{self_layout, 0, 0, (std::numeric_limits::max)()}; return const_subarray{new_layout, types::base_}; } @@ -3156,11 +3157,11 @@ struct const_subarray // NOLINT(fuchsia-multiple-inhe public: template::template rebind> - constexpr auto static_array_cast() const -> subarray { // name taken from std::static_pointer_cast + constexpr auto static_array_cast() const -> subarray { // name taken from std::static_pointer_cast return {this->layout(), static_cast(this->base_)}; } template::template rebind, class... Args> - constexpr auto static_array_cast(Args&&... args) const -> subarray { // name taken from std::static_pointer_cast + constexpr auto static_array_cast(Args&&... args) const -> subarray { // name taken from std::static_pointer_cast return {this->layout(), P2{this->base_, std::forward(args)...}}; } @@ -3278,8 +3279,7 @@ constexpr auto static_array_cast(Array&& self, Args&&... args) -> decltype(auto) } template -class array_ref // TODO(correaa) : inheredit from multi::partially_ordered2, void>? -: public subarray< +class array_ref : public subarray< T, D, ElementPtr, std::conditional_t<(D == 1), // continuous_layout<1, typename std::pointer_traits::difference_type>, @@ -3303,7 +3303,6 @@ class array_ref // TODO(correaa) : inheredit from multi::partially_ordered2{}>, decltype(multi::detail::explicit_cast(std::declval()))* = nullptr> constexpr explicit array_ref(array_ref&& other) - : subarray{other.layout(), ElementPtr{std::move(other).base()}} {} // cppcheck-suppress internalAstError ; bug in cppcheck 2.13.0 + : subarray_base(other.layout(), ElementPtr{std::move(other).base()}) {} // cppcheck-suppress internalAstError ; bug in cppcheck 2.13.0 template{}>, decltype(multi::detail::implicit_cast(std::declval()))* = nullptr> // cppcheck-suppress noExplicitConstructor ; to allow terse syntax constexpr /*implicit*/ array_ref(array_ref&& other) // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) - : subarray_base{other.layout(), ElementPtr{std::move(other).base()}} {} + : subarray_base(other.layout(), ElementPtr{std::move(other).base()}) {} constexpr array_ref(ElementPtr dat, ::boost::multi::extensions_t const& xs) /*noexcept*/ // TODO(correa) eliminate this ctor - : subarray_base{typename subarray::types::layout_t(xs), dat} {} + : subarray_base(typename subarray_base::types::layout_t(xs), dat) {} // constexpr array_ref(typename array_ref::extensions_type extensions, typename array_ref::element_ptr dat) noexcept // : subarray{typename array_ref::types::layout_t{extensions}, dat} {} @@ -3434,10 +3433,8 @@ class array_ref // TODO(correaa) : inheredit from multi::partially_ordered2 -// constexpr auto operator=(array_ref const& other)& -> array_ref& { assert( this->extensions() == other.extensions() ); - // MULTI_MARK_SCOPE(std::string{"multi::operator= D="}+std::to_string(D)+" from "+typeid(TT).name()+" to "+typeid(T).name() ); adl_copy_n(other.data_elements(), other.num_elements(), this->data_elements()); return *this; } @@ -3579,7 +3576,7 @@ class array_ref // TODO(correaa) : inheredit from multi::partially_ordered2 auto serialize_structured_(Ar& arxiv, unsigned int const version) { - subarray::serialize(arxiv, version); + subarray_base::serialize(arxiv, version); } template auto serialize_flat_(Archive& arxiv, unsigned int const /*version*/) { @@ -3634,7 +3631,7 @@ struct array_ptr using basic_ptr = subarray_ptr::layout_t>; constexpr array_ptr(Ptr data, multi::extensions_t extensions) - : basic_ptr{data, multi::layout_t{extensions}} {} + : basic_ptr{data, typename array_ref::layout_t(extensions)} {} constexpr explicit array_ptr(std::nullptr_t nil) : array_ptr{nil, multi::extensions_t{}} {} diff --git a/include/boost/multi/detail/layout.hpp b/include/boost/multi/detail/layout.hpp index 3901d8151..71b6d18c0 100644 --- a/include/boost/multi/detail/layout.hpp +++ b/include/boost/multi/detail/layout.hpp @@ -503,12 +503,14 @@ struct monostate : equality_comparable { friend BOOST_MULTI_HD constexpr auto operator==(monostate const& /*self*/, monostate const& /*other*/) {return true;} }; -template +template class continuous_layout; template class continuous_layout<1, SSize> { public: + continuous_layout() = default; + using dimensionality_type = multi::dimensionality_t; using rank = std::integral_constant; static constexpr dimensionality_type rank_v = rank::value; @@ -522,40 +524,54 @@ class continuous_layout<1, SSize> { using index_extension = multi::extension_t; using extension_type = index_extension; - auto extension() const -> extension_type; + constexpr auto extension() const { return extension_type{offset_, offset_ + nelems_}; } using extensions_type = multi::index_extensions<1>; - auto extensions() const -> extensions_type = delete; + auto extensions() const -> extensions_type { return extensions_type{extension()}; }; - auto is_empty() const -> bool = delete; - auto empty() const -> bool = delete; + constexpr auto is_empty() const -> bool { return false; } + constexpr auto empty() const -> bool { return is_empty(); } auto is_compact() const = delete; auto sub() const -> layout_t<0, SSize> = delete; - using stride_type = void; // perhaps std::integral_constant - auto stride() const -> stride_type; + using stride_type = std::integral_constant; + auto stride() const -> stride_type { return {}; } - using num_elements_type = void; - auto num_elements() const -> num_elements_type; + using num_elements_type = size_type; + auto num_elements() const -> num_elements_type { return nelems_; } - using offset_type = void; - auto offset() const -> offset_type; + using offset_type = difference_type; + auto offset() const -> offset_type { return offset_; } using offsets_type = void; auto offsets() const -> offsets_type; - using strides_type = void; // perhaps std::integral_constant + using strides_type = multi::tuple >; auto strides() const -> stride_type; - auto size() const -> size_type; + auto size() const -> size_type { return nelems_; } - using nelems_type = void; // perhaps std::integral_constant - auto nelems() const -> nelems_type; + using nelems_type = size_type; + constexpr auto nelems() const -> nelems_type { return nelems_; } using sizes_type = void; auto sizes() const -> sizes_type = delete; + + constexpr auto operator()(index const& idx) const -> difference_type { return idx - offset_; } + + private: + // BOOST_MULTI_NO_UNIQUE_ADDRESS sub_type sub_ ; + // BOOST_MULTI_NO_UNIQUE_ADDRESS stride_type stride_; // = {}; + offset_type offset_; + nelems_type nelems_; + + public: + constexpr explicit continuous_layout(extensions_type const& xs) : + offset_{xs.get<0>().front()}, + nelems_{xs.get<0>().size()} + {} }; template @@ -622,7 +638,7 @@ struct layout_t<0, SSize> [[nodiscard]] constexpr auto offsets() const {return offsets_type{};} [[nodiscard]] constexpr auto nelemss() const {return nelemss_type{};} - constexpr auto operator()() const {return offset_;} + constexpr auto operator()() const { return offset_; } // constexpr explicit operator offset_type() const {return offset_;} constexpr auto stride() const -> stride_type = delete; @@ -721,6 +737,20 @@ struct layout_t public: layout_t() = default; + template< + class OtherLayout, + class = decltype(sub_type {std::declval().sub()}), + class = decltype(stride_type{std::declval().stride()}), + class = decltype(offset_type{std::declval().offset()}), + class = decltype(nelems_type{std::declval().nelems()}) + > + BOOST_MULTI_HD constexpr explicit layout_t(OtherLayout const& other) : + sub_{other.sub()}, + stride_{other.stride()}, + offset_{other.offset()}, + nelems_{other.nelems()} + {} + BOOST_MULTI_HD constexpr explicit layout_t(extensions_type const& extensions) : sub_{ std::apply( @@ -760,9 +790,9 @@ struct layout_t constexpr auto operator[](index idx) const {return at_aux_(idx);} template - constexpr auto operator()(index idx, Indices... rest) const {return operator[](idx)(rest...);} - constexpr auto operator()(index idx) const {return at_aux_(idx);} - constexpr auto operator()() const {return *this;} + constexpr auto operator()(index idx, Indices... rest) const { return operator[](idx)(rest...); } + constexpr auto operator()(index idx) const { return at_aux_(idx); } + constexpr auto operator()() const { return *this; } BOOST_MULTI_HD constexpr auto sub() & -> sub_type & {return sub_ ;} BOOST_MULTI_HD constexpr auto sub() const& -> sub_type const& {return sub_ ;} diff --git a/include/boost/multi/detail/tuple_zip.hpp b/include/boost/multi/detail/tuple_zip.hpp index 44c83369b..c206bc0d7 100644 --- a/include/boost/multi/detail/tuple_zip.hpp +++ b/include/boost/multi/detail/tuple_zip.hpp @@ -14,7 +14,8 @@ namespace boost::multi { // NOLINT(modernize-concat-nested-namespaces) keep c++14 compat namespace detail { -template class tuple; +// we need a custom tuple type, so some fundamental types (e.g. iterators) are trivially constructible +template class tuple; // TODO(correaa) consider renaming it to `tpl` template<> class tuple<> { // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) public: From 7750c5c2bf808cbbf52785c87f2525dd62843e68 Mon Sep 17 00:00:00 2001 From: Alfredo Correa Date: Thu, 2 Jan 2025 15:41:54 -0800 Subject: [PATCH 3/4] remove mutation in slice_aux --- include/boost/multi/array_ref.hpp | 15 ++++++++------- include/boost/multi/detail/layout.hpp | 11 ++++++----- include/boost/multi/detail/tuple_zip.hpp | 4 ++++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/include/boost/multi/array_ref.hpp b/include/boost/multi/array_ref.hpp index f1e9b9e09..f7cd03a42 100644 --- a/include/boost/multi/array_ref.hpp +++ b/include/boost/multi/array_ref.hpp @@ -2815,13 +2815,14 @@ struct const_subarray // NOLINT(fuchsia-multiple-inhe #endif BOOST_MULTI_HD constexpr auto sliced_aux_(index first, index last) const { - typename types::layout_t new_layout = this->layout(); - if(this->is_empty()) { - assert(first == last); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay,hicpp-no-array-decay) : normal in a constexpr function - new_layout.nelems() = 0; // TODO(correaa) : don't use mutation - } else { - (new_layout.nelems() /= this->size())*=(last - first); - } + auto const new_layout = typename types::layout_t{ + this->layout().sub(), + this->layout().stride(), + this->layout().offset(), + (this->is_empty())? + 0: + this->layout().nelems() / this->size() * (last - first) + }; return const_subarray{new_layout, this->base_ + (first*this->layout().stride() - this->layout().offset())}; } diff --git a/include/boost/multi/detail/layout.hpp b/include/boost/multi/detail/layout.hpp index 71b6d18c0..468cc91b0 100644 --- a/include/boost/multi/detail/layout.hpp +++ b/include/boost/multi/detail/layout.hpp @@ -534,7 +534,7 @@ class continuous_layout<1, SSize> { auto is_compact() const = delete; - auto sub() const -> layout_t<0, SSize> = delete; + auto sub() const { return layout_t<0, SSize>{}; } using stride_type = std::integral_constant; auto stride() const -> stride_type { return {}; } @@ -568,10 +568,11 @@ class continuous_layout<1, SSize> { nelems_type nelems_; public: - constexpr explicit continuous_layout(extensions_type const& xs) : - offset_{xs.get<0>().front()}, - nelems_{xs.get<0>().size()} - {} + constexpr explicit continuous_layout(extensions_type const& xs) + : offset_{xs.get<0>().front()}, + nelems_{xs.get<0>().size()} {} + + constexpr auto reindex(index idx) -> auto& { offset_ = idx * stride(); return *this; } }; template diff --git a/include/boost/multi/detail/tuple_zip.hpp b/include/boost/multi/detail/tuple_zip.hpp index c206bc0d7..237632bce 100644 --- a/include/boost/multi/detail/tuple_zip.hpp +++ b/include/boost/multi/detail/tuple_zip.hpp @@ -58,6 +58,10 @@ template class tuple : tuple { // NOLI // cppcheck-suppress noExplicitConstructor ; allow bracket init in function argument // NOLINTNEXTLINE(runtime/explicit) constexpr tuple(T0 head, Ts... tail) : tail_type{tail...}, head_{head} {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) to allow bracket function calls + // cppcheck-suppress noExplicitConstructor ; allow bracket init in function argument // NOLINTNEXTLINE(runtime/explicit) + template =0> + constexpr tuple(TT0 head) : tail_type{}, head_{head} {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) to allow bracket function calls + // cppcheck-suppress noExplicitConstructor ; allow bracket init in function argument // NOLINTNEXTLINE(runtime/explicit) constexpr explicit tuple(::std::tuple other) : tuple(::std::apply([](auto... es) {return tuple(es...);}, other)) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) From 91346e8e9bb7775b2a4d6a1f321508964f7f5955 Mon Sep 17 00:00:00 2001 From: Alfredo Correa Date: Thu, 2 Jan 2025 16:14:27 -0800 Subject: [PATCH 4/4] fix enable_if --- include/boost/multi/detail/tuple_zip.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/multi/detail/tuple_zip.hpp b/include/boost/multi/detail/tuple_zip.hpp index 237632bce..80c4fcffb 100644 --- a/include/boost/multi/detail/tuple_zip.hpp +++ b/include/boost/multi/detail/tuple_zip.hpp @@ -59,7 +59,7 @@ template class tuple : tuple { // NOLI constexpr tuple(T0 head, Ts... tail) : tail_type{tail...}, head_{head} {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) to allow bracket function calls // cppcheck-suppress noExplicitConstructor ; allow bracket init in function argument // NOLINTNEXTLINE(runtime/explicit) - template =0> + template =0> constexpr tuple(TT0 head) : tail_type{}, head_{head} {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) to allow bracket function calls // cppcheck-suppress noExplicitConstructor ; allow bracket init in function argument // NOLINTNEXTLINE(runtime/explicit)