Since the standard range adaptors are specified to derive from the empty class view_base, making their first data member store the underlying view is suboptimal, for if the underlying view also derives from view_base then the two view_base subobjects will be adjacent, thus preventing the compiler from applying the empty base optimization to elide away the storage for these two empty bases.
This patch improves the situation by declaring the _M_base data member last instead of first in each range adaptor that has more than one data member, so that the empty base optimization can apply more often. Tested on x86_64-pc-linux-gnu with and wihout -m32. libstdc++-v3/ChangeLog: * include/std/ranges (filter_view::_M_base): Declare this data member last. (transform_view::_M_base): Likewise. (take_view::_M_base): Likewise. (take_while_view::_M_base): Likewise. (drop_view::_M_base): Likewise. (drop_while_view::_M_base): Likewise. (join_view::_M_base): Likewise. (split_view::_M_base): Likewise. * testsuite/std/ranges/adaptors/sizeof.cc: Adjust expected sizes. --- libstdc++-v3/include/std/ranges | 17 ++++++++--------- .../testsuite/std/ranges/adaptors/sizeof.cc | 18 +++++++++--------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 964a2b616a6..6fd8a85c2bf 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -1250,9 +1250,9 @@ namespace views { return __y.__equal(__x); } }; - _Vp _M_base = _Vp(); [[no_unique_address]] __detail::__box<_Pred> _M_pred; [[no_unique_address]] __detail::_CachedPosition<_Vp> _M_cached_begin; + _Vp _M_base = _Vp(); public: filter_view() = default; @@ -1588,8 +1588,8 @@ namespace views friend _Sentinel<!_Const>; }; - _Vp _M_base = _Vp(); [[no_unique_address]] __detail::__box<_Fp> _M_fun; + _Vp _M_base = _Vp(); public: transform_view() = default; @@ -1695,8 +1695,8 @@ namespace views friend _Sentinel<!_Const>; }; - _Vp _M_base = _Vp(); range_difference_t<_Vp> _M_count = 0; + _Vp _M_base = _Vp(); public: take_view() = default; @@ -1842,8 +1842,8 @@ namespace views friend _Sentinel<!_Const>; }; - _Vp _M_base = _Vp(); [[no_unique_address]] __detail::__box<_Pred> _M_pred; + _Vp _M_base = _Vp(); public: take_while_view() = default; @@ -1902,8 +1902,8 @@ namespace views class drop_view : public view_interface<drop_view<_Vp>> { private: - _Vp _M_base = _Vp(); range_difference_t<_Vp> _M_count = 0; + _Vp _M_base = _Vp(); // ranges::next(begin(base), count, end(base)) is O(1) if _Vp satisfies // both random_access_range and sized_range. Otherwise, cache its result. @@ -2002,9 +2002,9 @@ namespace views class drop_while_view : public view_interface<drop_while_view<_Vp, _Pred>> { private: - _Vp _M_base = _Vp(); [[no_unique_address]] __detail::__box<_Pred> _M_pred; [[no_unique_address]] __detail::_CachedPosition<_Vp> _M_cached_begin; + _Vp _M_base = _Vp(); public: drop_while_view() = default; @@ -2300,12 +2300,11 @@ namespace views friend _Sentinel<!_Const>; }; - _Vp _M_base = _Vp(); - // XXX: _M_inner is "present only when !is_reference_v<_InnerRange>" [[no_unique_address]] __detail::__maybe_present_t<!is_reference_v<_InnerRange>, views::all_t<_InnerRange>> _M_inner; + _Vp _M_base = _Vp(); public: join_view() = default; @@ -2680,8 +2679,8 @@ namespace views { ranges::iter_swap(__x._M_i_current(), __y._M_i_current()); } }; - _Vp _M_base = _Vp(); _Pattern _M_pattern = _Pattern(); + _Vp _M_base = _Vp(); // XXX: _M_current is "present only if !forward_range<V>" [[no_unique_address]] diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc index 5fb1ab7e4da..a7f622bb725 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc @@ -33,17 +33,17 @@ using V = ranges::subrange<int*, int*>; constexpr auto ptr = sizeof(int*); static_assert(sizeof(V) == 2*ptr); -static_assert(sizeof(ranges::take_view<V>) == 4*ptr); -static_assert(sizeof(ranges::drop_view<V>) == 4*ptr); +static_assert(sizeof(ranges::take_view<V>) == 3*ptr); +static_assert(sizeof(ranges::drop_view<V>) == 3*ptr); -static_assert(sizeof(ranges::filter_view<V, decltype(&pred_f)>) == 5*ptr); -static_assert(sizeof(ranges::take_while_view<V, decltype(&pred_f)>) == 4*ptr); -static_assert(sizeof(ranges::drop_while_view<V, decltype(&pred_f)>) == 5*ptr); -static_assert(sizeof(ranges::transform_view<V, decltype(&func_f)>) == 4*ptr); +static_assert(sizeof(ranges::filter_view<V, decltype(&pred_f)>) == 4*ptr); +static_assert(sizeof(ranges::take_while_view<V, decltype(&pred_f)>) == 3*ptr); +static_assert(sizeof(ranges::drop_while_view<V, decltype(&pred_f)>) == 4*ptr); +static_assert(sizeof(ranges::transform_view<V, decltype(&func_f)>) == 3*ptr); -static_assert(sizeof(ranges::filter_view<V, decltype(pred_l)>) == 4*ptr); +static_assert(sizeof(ranges::filter_view<V, decltype(pred_l)>) == 3*ptr); static_assert(sizeof(ranges::take_while_view<V, decltype(pred_l)>) == 3*ptr); -static_assert(sizeof(ranges::drop_while_view<V, decltype(pred_l)>) == 4*ptr); +static_assert(sizeof(ranges::drop_while_view<V, decltype(pred_l)>) == 3*ptr); static_assert(sizeof(ranges::transform_view<V, decltype(func_l)>) == 3*ptr); -static_assert(sizeof(ranges::split_view<V, std::string_view>) == 5*ptr); +static_assert(sizeof(ranges::split_view<V, std::string_view>) == 4*ptr); -- 2.28.0.618.g9bc233ae1c