From 5a967e3f2a386e140814022e51ecd5decdd41d2d Mon Sep 17 00:00:00 2001 From: Loic Gouarin Date: Thu, 29 Oct 2026 15:45:41 +0100 Subject: [PATCH] Use copy_n for shape copies into fixed-size containers GCC 13 and 15 emit -Warray-bounds at -O2/-O3 on a std::copy from a std::array member into another std::array. When the array is a member at a non-zero offset, GCC does not fold the length of the source range and warns on the memmove branch that libstdc++ keeps for ranges of more than one element. That branch never runs. User code as simple as assigning 0.5 * (even + odd) to a 1-D view gets the warning. Minimal reproducer without xtensor: struct holder { std::size_t m_size; std::array m_shape; }; std::array f(const holder& h) { std::array s; std::copy(h.m_shape.cbegin(), h.m_shape.cend(), s.begin()); return s; } std::copy_n takes the count directly, and size() is a constant for std::array, so GCC drops the branch. In each changed site the destination is sized from the same source, so the number of copied elements does not change. Changed sites: xfunction::broadcast_shape, the xbroadcast constructor, flip (shape and strides) and average with multi-dimensional weights. --- include/xtensor/core/xfunction.hpp | 2 +- include/xtensor/core/xmath.hpp | 2 +- include/xtensor/misc/xmanipulation.hpp | 4 ++-- include/xtensor/views/xbroadcast.hpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/xtensor/core/xfunction.hpp b/include/xtensor/core/xfunction.hpp index 99144b377..0cc797769 100644 --- a/include/xtensor/core/xfunction.hpp +++ b/include/xtensor/core/xfunction.hpp @@ -712,7 +712,7 @@ namespace xt { if (m_cache.is_initialized && reuse_cache) { - std::copy(m_cache.shape.cbegin(), m_cache.shape.cend(), shape.begin()); + std::copy_n(m_cache.shape.cbegin(), m_cache.shape.size(), shape.begin()); return m_cache.is_trivial; } else diff --git a/include/xtensor/core/xmath.hpp b/include/xtensor/core/xmath.hpp index 6b86c4b74..e40536651 100644 --- a/include/xtensor/core/xmath.hpp +++ b/include/xtensor/core/xmath.hpp @@ -1960,7 +1960,7 @@ namespace xt ); } - std::copy(e.shape().begin(), e.shape().end(), broadcast_shape.begin()); + std::copy_n(e.shape().begin(), e.shape().size(), broadcast_shape.begin()); } constexpr layout_type L = default_assignable_layout(std::decay_t::static_layout); diff --git a/include/xtensor/misc/xmanipulation.hpp b/include/xtensor/misc/xmanipulation.hpp index db4125e01..cdf6139b6 100644 --- a/include/xtensor/misc/xmanipulation.hpp +++ b/include/xtensor/misc/xmanipulation.hpp @@ -840,12 +840,12 @@ namespace xt shape_type shape; resize_container(shape, e.shape().size()); - std::copy(e.shape().cbegin(), e.shape().cend(), shape.begin()); + std::copy_n(e.shape().cbegin(), e.shape().size(), shape.begin()); get_strides_t strides; decltype(auto) old_strides = detail::get_strides(e); resize_container(strides, old_strides.size()); - std::copy(old_strides.cbegin(), old_strides.cend(), strides.begin()); + std::copy_n(old_strides.cbegin(), old_strides.size(), strides.begin()); strides[axis] *= -1; std::size_t offset = static_cast( diff --git a/include/xtensor/views/xbroadcast.hpp b/include/xtensor/views/xbroadcast.hpp index ae092c2bb..24951e02b 100644 --- a/include/xtensor/views/xbroadcast.hpp +++ b/include/xtensor/views/xbroadcast.hpp @@ -293,7 +293,7 @@ namespace xt XTENSOR_THROW(xt::broadcast_error, "Broadcast shape has fewer elements than original expression."); } xt::resize_container(m_shape, s.size()); - std::copy(s.begin(), s.end(), m_shape.begin()); + std::copy_n(s.begin(), s.size(), m_shape.begin()); xt::broadcast_shape(m_e.shape(), m_shape); }