On Fri, 18 Oct 2024, Patrick Palka wrote:

> On Fri, 18 Oct 2024, Jonathan Wakely wrote:
> 
> > On 16/10/24 21:39 -0400, Patrick Palka wrote:
> > > On Tue, 15 Oct 2024, Jonathan Wakely wrote:
> > > > +#if __cplusplus < 201103L
> > > > +
> > > > +  // True if we can unwrap _Iter to get a pointer by using
> > > > std::__niter_base.
> > > > +  template<typename _Iter>
> > > > +    struct __unwrappable_niter
> > > > +    {
> > > > +      template<typename> struct __is_ptr { enum { __value = 0 }; };
> > > > +      template<typename _Tp> struct __is_ptr<_Tp*> { enum { __value = 1
> > > > }; };
> > > > +
> > > > +      typedef __decltype(std::__niter_base(*(_Iter*)0)) _Base;
> > > > +
> > > > +      enum { __value = __is_ptr<_Base>::__value };
> > > > +    };
> > > 
> > > It might be slightly cheaper to define this without the nested class
> > > template as:
> > > 
> > >  template<typename _Iter, typename _Base =
> > > __decltype(std::__niter_base(*(_Iter*)0))>
> > >  struct __unwrappable_niter
> > >  { enum { __value = false }; };
> > > 
> > >  template<typename _Iter, typename _Tp>
> > >  struct __unwrappable_niter<_Iter, _Tp*>
> > >  { enum { __value = true }; };
> 
> One minor nit, we might as well use 'value' since it's a reserved name
> even in C++98?

Whoops just saw that you already pushed this, never mind this tiny nit
then :)

> 
> > 
> > Nice. I think after spending a while failing to make any C++98
> > metaprogramming work for __memcpyable in cpp_type_traits.h I was not
> > in the mood for fighting C++98 any more :-) But this works well.
> 
> > 
> > > > +
> > > > +  // Use template specialization for C++98 when 'if constexpr' can't be
> > > > used.
> > > > +  template<bool _CanMemcpy>
> > > >      struct __uninitialized_copy
> > > >      {
> > > >        template<typename _InputIterator, typename _ForwardIterator>
> > > > @@ -186,53 +172,150 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > >    template<>
> > > >      struct __uninitialized_copy<true>
> > > >      {
> > > > +      // Overload for generic iterators.
> > > >        template<typename _InputIterator, typename _ForwardIterator>
> > > >          static _ForwardIterator
> > > >          __uninit_copy(_InputIterator __first, _InputIterator __last,
> > > >                       _ForwardIterator __result)
> > > > -        { return std::copy(__first, __last, __result); }
> > > > -    };
> > > > +       {
> > > > +         if (__unwrappable_niter<_InputIterator>::__value
> > > > +               && __unwrappable_niter<_ForwardIterator>::__value)
> > > > +           {
> > > > +             __uninit_copy(std::__niter_base(__first),
> > > > +                           std::__niter_base(__last),
> > > > +                           std::__niter_base(__result));
> > > > +             std::advance(__result, std::distance(__first, __last));
> > > > +             return __result;
> > > > +           }
> > > > +         else
> > > > +           return std::__do_uninit_copy(__first, __last, __result);
> > > > +       }
> > > > 
> > > > +      // Overload for pointers.
> > > > +      template<typename _Tp, typename _Up>
> > > > +       static _Up*
> > > > +       __uninit_copy(_Tp* __first, _Tp* __last, _Up* __result)
> > > > +       {
> > > > +         // Ensure that we don't successfully memcpy in cases that 
> > > > should be
> > > > +         // ill-formed because is_constructible<_Up, _Tp&> is false.
> > > > +         typedef __typeof__(static_cast<_Up>(*__first)) __check
> > > > +           __attribute__((__unused__));
> > > > +
> > > > +         if (const ptrdiff_t __n = __last - __first)
> > > 
> > > Do we have to worry about the __n == 1 case here like in the C++11 code
> > > path?
> > 
> > Actually I think we don't need to worry about it in either case.
> > 
> > C++20 had a note in [specialized.algorithms.general]/3 that said:
> > 
> >   [Note 1: When invoked on ranges of potentially-overlapping subobjects
> >   ([intro.object]), the algorithms specified in [specialized.algorithms]
> >   result in undefined behavior. — end note]
> > 
> > The reason is that the uninitialized algos create new objects at the
> > specified storage locations, and creating new objects reuses storage,
> > which ends the lifetime of any other objects in that storage. That
> > includes any objects that were in tail padding within that storage.
> > 
> > See Casey's Feb 2023 comment at
> > https://github.com/cplusplus/draft/issues/6143
> > 
> > That note was removed for C++23 (which is unfortunate IMHO), but the
> > algos still reuse storage by creating new objects, and so still end
> > the lifetime of potentially-overlapping subobjects within that
> > storage.
> > 
> > For std::copy there are no new objects created, and the effects are
> > specified in terms of assignment, which does not reuse storage. A
> > compiler-generated trivial copy assignment operator is careful to not
> > overwrite tail padding, so we can't use memmove if it would produce
> > different effects.
> > 
> > tl;dr I think I can remove the __n == 1 handling from the C++11 paths.
> 
> Nice, makes sense
> 
> > 
> > > > +           {
> > > > +             __builtin_memcpy(__result, __first, __n * sizeof(_Tp));
> > > > +             __result += __n;
> > > > +           }
> > > > +         return __result;
> > > > +       }
> > > > +    };
> > > > +#endif
> > > >    /// @endcond
> > > > 
> > > > +#pragma GCC diagnostic push
> > > > +#pragma GCC diagnostic ignored "-Wc++17-extensions"
> > > >    /**
> > > >     *  @brief Copies the range [first,last) into result.
> > > >     *  @param  __first  An input iterator.
> > > >     *  @param  __last   An input iterator.
> > > > -   *  @param  __result An output iterator.
> > > > -   *  @return   __result + (__first - __last)
> > > > +   *  @param  __result A forward iterator.
> > > > +   *  @return   __result + (__last - __first)
> > > >     *
> > > > -   *  Like copy(), but does not require an initialized output range.
> > > > +   *  Like std::copy, but does not require an initialized output range.
> > > >    */
> > > >    template<typename _InputIterator, typename _ForwardIterator>
> > > >      inline _ForwardIterator
> > > >      uninitialized_copy(_InputIterator __first, _InputIterator __last,
> > > >                        _ForwardIterator __result)
> > > >      {
> > > > +      // We can use memcpy to copy the ranges under these conditions:
> > > > +      //
> > > > +      // _ForwardIterator and _InputIterator are both contiguous
> > > > iterators,
> > > > +      // so that we can turn them into pointers to pass to memcpy.
> > > > +      // Before C++20 we can't detect all contiguous iterators, so we
> > > > only
> > > > +      // handle built-in pointers and __normal_iterator<T*, C> types.
> > > > +      //
> > > > +      // The value types of both iterators are trivially-copyable 
> > > > types,
> > > > +      // so that memcpy is not undefined and can begin the lifetime of
> > > > +      // new objects in the output range.
> > > > +      //
> > > > +      // Finally, memcpy from the source type, S, to the destination
> > > > type, D,
> > > > +      // must give the same value as initialization of D from S would
> > > > give.
> > > > +      // We require is_trivially_constructible<D, S> to be true, but 
> > > > that
> > > > is
> > > > +      // not sufficient. Some cases of trivial initialization are not
> > > > just a
> > > > +      // bitwise copy, even when sizeof(D) == sizeof(S),
> > > > +      // e.g. bit_cast<unsigned>(1.0f) != 1u because the corresponding
> > > > bits
> > > > +      // of the value representations do not have the same meaning.
> > > > +      // We cannot tell when this condition is true in general,
> > > > +      // so we rely on the __memcpyable trait.
> > > > +
> > > > +#if __cplusplus >= 201103L
> > > > +      using _Dest = decltype(std::__niter_base(__result));
> > > > +      using _Src = decltype(std::__niter_base(__first));
> > > > +      using _ValT = typename
> > > > iterator_traits<_ForwardIterator>::value_type;
> > > > +
> > > > +      if constexpr (!__is_trivially_constructible(_ValT,
> > > > decltype(*__first)))
> > > > +       return std::__do_uninit_copy(__first, __last, __result);
> > > > +      else if constexpr (__memcpyable<_Dest, _Src>::__value)
> > > > +       {
> > > > +         ptrdiff_t __n = __last - __first;
> > > > +         if (__builtin_expect(__n > 1, true))
> > > 
> > > Could we use [[__likely__]] instead?
> > 
> > We could indeed.
> > 
> > > > +           {
> > > > +             using _ValT = typename remove_pointer<_Src>::type;
> > > > +             __builtin_memcpy(std::__niter_base(__result),
> > > > +                              std::__niter_base(__first),
> > > > +                              __n * sizeof(_ValT));
> > > > +             __result += __n;
> > > > +           }
> > > > +         else if (__n == 1) // memcpy could overwrite tail padding
> > > > +           std::_Construct(__result++, *__first);
> > > > +         return __result;
> > > > +       }
> > > > +#if __cpp_lib_concepts
> > > > +      else if constexpr (contiguous_iterator<_ForwardIterator>
> > > > +                          && contiguous_iterator<_InputIterator>)
> > > > +       {
> > > > +         using _DestPtr = decltype(std::to_address(__result));
> > > > +         using _SrcPtr = decltype(std::to_address(__first));
> > > > +         if constexpr (__memcpyable<_DestPtr, _SrcPtr>::__value)
> > > > +           {
> > > > +             if (auto __n = __last - __first; __n > 1) [[likely]]
> > > > +               {
> > > > +                 void* __dest = std::to_address(__result);
> > > > +                 const void* __src = std::to_address(__first);
> > > > +                 size_t __nbytes = __n * 
> > > > sizeof(remove_pointer_t<_DestPtr>);
> > > > +                 __builtin_memmove(__dest, __src, __nbytes);
> > > 
> > > Why do we need to use memmove instead of memcpy here?
> > 
> > Looks like I just forgot to change it here when I realised that we can
> > use memcpy instead of memmove. Oops.
> > 
> > > > +                 __result += __n;
> > > > +               }
> > > > +             else if (__n == 1) // memcpy could overwrite tail padding
> > > > +               std::construct_at(std::to_address(__result++), 
> > > > *__first);
> > > > +             return __result;
> > > > +           }
> > > > +         else
> > > > +           return std::__do_uninit_copy(__first, __last, __result);
> > > > +       }
> > > > +#endif
> > > > +      else
> > > > +       return std::__do_uninit_copy(__first, __last, __result);
> > > > +#else // C++98
> > > >        typedef typename iterator_traits<_InputIterator>::value_type
> > > >         _ValueType1;
> > > >        typedef typename iterator_traits<_ForwardIterator>::value_type
> > > >         _ValueType2;
> > > > 
> > > > -      // _ValueType1 must be trivially-copyable to use memmove, so 
> > > > don't
> > > > -      // bother optimizing to std::copy if it isn't.
> > > > -      // XXX Unnecessary because std::copy would check it anyway?
> > > > -      const bool __can_memmove = __is_trivial(_ValueType1);
> > > > +      const bool __can_memcpy
> > > > +       = __memcpyable<_ValueType1*, _ValueType2*>::__value
> > > > +           && __is_trivially_constructible(_ValueType2,
> > > > __decltype(*__first));
> > > > 
> > > > -#if __cplusplus < 201103L
> > > > -      typedef typename iterator_traits<_InputIterator>::reference 
> > > > _From;
> > > > -#else
> > > > -      using _From = decltype(*__first);
> > > > +      return __uninitialized_copy<__can_memcpy>::
> > > > +              __uninit_copy(__first, __last, __result);
> > > >  #endif
> > > > -      const bool __assignable
> > > > -       = _GLIBCXX_USE_ASSIGN_FOR_INIT(_ValueType2, _From);
> > > > -
> > > > -      return std::__uninitialized_copy<__can_memmove && __assignable>::
> > > > -       __uninit_copy(__first, __last, __result);
> > > >      }
> > > > +#pragma GCC diagnostic pop
> > > > 
> > > >    /// @cond undocumented
> > > > 
> > > > +  // This is the default implementation of std::uninitialized_fill.
> > > >    template<typename _ForwardIterator, typename _Tp>
> > > >      _GLIBCXX20_CONSTEXPR void
> > > >      __do_uninit_fill(_ForwardIterator __first, _ForwardIterator __last,
> > > > @@ -244,12 +327,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > >        __guard.release();
> > > >      }
> > > > 
> > > > -  template<bool _TrivialValueType>
> > > > +#if __cplusplus < 201103L
> > > > +  // Use template specialization for C++98 when 'if constexpr' can't be
> > > > used.
> > > > +  template<bool _CanMemset>
> > > >      struct __uninitialized_fill
> > > >      {
> > > >        template<typename _ForwardIterator, typename _Tp>
> > > > -        static void
> > > > -        __uninit_fill(_ForwardIterator __first, _ForwardIterator 
> > > > __last,
> > > > +       static void
> > > > +       __uninit_fill(_ForwardIterator __first, _ForwardIterator __last,
> > > >                       const _Tp& __x)
> > > >         { std::__do_uninit_fill(__first, __last, __x); }
> > > >      };
> > > > @@ -257,56 +342,129 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > >    template<>
> > > >      struct __uninitialized_fill<true>
> > > >      {
> > > > +      // Overload for generic iterators.
> > > >        template<typename _ForwardIterator, typename _Tp>
> > > > -        static void
> > > > -        __uninit_fill(_ForwardIterator __first, _ForwardIterator 
> > > > __last,
> > > > +       static void
> > > > +       __uninit_fill(_ForwardIterator __first, _ForwardIterator __last,
> > > >                       const _Tp& __x)
> > > > -        { std::fill(__first, __last, __x); }
> > > > -    };
> > > > +       {
> > > > +         if (__unwrappable_niter<_ForwardIterator>::__value)
> > > > +           __uninit_fill(std::__niter_base(__first),
> > > > +                         std::__niter_base(__last),
> > > > +                         __x);
> > > > +         else
> > > > +           std::__do_uninit_copy(__first, __last, __x);
> > > > +       }
> > > > 
> > > > +      // Overload for pointers.
> > > > +      template<typename _Up, typename _Tp>
> > > > +       static void
> > > > +       __uninit_fill(_Up* __first, _Up* __last, const _Tp& __x)
> > > > +       {
> > > > +         // Ensure that we don't successfully memset in cases that 
> > > > should be
> > > > +         // ill-formed because is_constructible<_Up, const _Tp&> is 
> > > > false.
> > > > +         typedef __typeof__(static_cast<_Up>(__x)) __check
> > > > +           __attribute__((__unused__));
> > > > +
> > > > +         if (__first != __last)
> > > > +           __builtin_memset(__first, (int)__x, __last - __first);
> > > 
> > > Maybe (unsigned char)__x for consistency with the C++11 code path?
> > 
> > Yes, I have no idea why I did (int) there.
> > 
> > > > +       }
> > > > +    };
> > > > +#endif
> > > >    /// @endcond
> > > > 
> > > >    /**
> > > >     *  @brief Copies the value x into the range [first,last).
> > > > -   *  @param  __first  An input iterator.
> > > > -   *  @param  __last   An input iterator.
> > > > +   *  @param  __first  A forward iterator.
> > > > +   *  @param  __last   A forward iterator.
> > > >     *  @param  __x      The source value.
> > > >     *  @return   Nothing.
> > > >     *
> > > > -   *  Like fill(), but does not require an initialized output range.
> > > > +   *  Like std::fill, but does not require an initialized output range.
> > > >    */
> > > >    template<typename _ForwardIterator, typename _Tp>
> > > >      inline void
> > > >      uninitialized_fill(_ForwardIterator __first, _ForwardIterator 
> > > > __last,
> > > >                        const _Tp& __x)
> > > >      {
> > > > +      // We would like to use memset to optimize this loop when 
> > > > possible.
> > > > +      // As for std::uninitialized_copy, the optimization requires
> > > > +      // contiguous iterators and trivially copyable value types,
> > > > +      // with the additional requirement that sizeof(_Tp) == 1 because
> > > > +      // memset only writes single bytes.
> > > > +
> > > > +      // FIXME: We could additionally enable this for 1-byte enums.
> > > > +      // Maybe any 1-byte Val if is_trivially_constructible<Val, const
> > > > T&>?
> > > > +
> > > >        typedef typename iterator_traits<_ForwardIterator>::value_type
> > > >         _ValueType;
> > > > 
> > > > -      // Trivial types do not need a constructor to begin their 
> > > > lifetime,
> > > > -      // so try to use std::fill to benefit from its memset 
> > > > optimization.
> > > > -      const bool __can_fill
> > > > -       = _GLIBCXX_USE_ASSIGN_FOR_INIT(_ValueType, const _Tp&);
> > > > +#if __cplusplus >= 201103L
> > > > +#pragma GCC diagnostic push
> > > > +#pragma GCC diagnostic ignored "-Wc++17-extensions"
> > > > +      if constexpr (__is_byte<_ValueType>::__value)
> > > > +       if constexpr (is_same<_ValueType, _Tp>::value
> > > > +                       || is_integral<_Tp>::value)
> > > > +         {
> > > > +           using _BasePtr = decltype(std::__niter_base(__first));
> > > > +           if constexpr (is_pointer<_BasePtr>::value)
> > > > +             {
> > > > +               void* __dest = std::__niter_base(__first);
> > > > +               ptrdiff_t __n = __last - __first;
> > > > +               if (__builtin_expect(__n > 0, true))
> > > > +                 __builtin_memset(__dest, (unsigned char)__x, __n);
> > > > +               return;
> > > > +             }
> > > > +#if __cpp_lib_concepts
> > > > +           else if constexpr (contiguous_iterator<_ForwardIterator>)
> > > > +             {
> > > > +               auto __dest = std::__to_address(__first);
> > > > +               auto __n = __last - __first;
> > > > +               if (__builtin_expect(__n > 0, true))
> > > > +                 __builtin_memset(__dest, (unsigned char)__x, __n);
> > > > +               return;
> > > > +             }
> > > > +#endif
> > > > +         }
> > > > +      std::__do_uninit_fill(__first, __last, __x);
> > > > +#pragma GCC diagnostic pop
> > > > +#else // C++98
> > > > +      const bool __can_memset = __is_byte<_ValueType>::__value
> > > > +                                 && __is_integer<_Tp>::__value;
> > > > 
> > > > -      std::__uninitialized_fill<__can_fill>::
> > > > -       __uninit_fill(__first, __last, __x);
> > > > +      __uninitialized_fill<__can_memset>::__uninit_fill(__first, 
> > > > __last,
> > > > __x);
> > > > +#endif
> > > >      }
> > > > 
> > > >    /// @cond undocumented
> > > > 
> > > > +  // This is the default implementation of std::uninitialized_fill_n.
> > > >    template<typename _ForwardIterator, typename _Size, typename _Tp>
> > > >      _GLIBCXX20_CONSTEXPR
> > > >      _ForwardIterator
> > > >      __do_uninit_fill_n(_ForwardIterator __first, _Size __n, const _Tp&
> > > > __x)
> > > >      {
> > > >        _UninitDestroyGuard<_ForwardIterator> __guard(__first);
> > > > -      for (; __n > 0; --__n, (void) ++__first)
> > > > +#if __cplusplus >= 201103L
> > > > +#pragma GCC diagnostic push
> > > > +#pragma GCC diagnostic ignored "-Wc++17-extensions"
> > > > +      if constexpr (is_integral<_Size>::value)
> > > > +       // Loop will never terminate if __n is negative.
> > > > +       __glibcxx_assert(__n >= 0);
> > > > +      else if constexpr (is_floating_point<_Size>::value)
> > > > +       // Loop will never terminate if __n is not an integer.
> > > > +       __glibcxx_assert(__n >= 0 && static_cast<size_t>(__n) == __n);
> > > > +#pragma GCC diagnostic pop
> > > > +#endif
> > > > +      for (; __n--; ++__first)
> > > >         std::_Construct(std::__addressof(*__first), __x);
> > > >        __guard.release();
> > > >        return __first;
> > > >      }
> > > > 
> > > > -  template<bool _TrivialValueType>
> > > > +#if __cplusplus < 201103L
> > > > +  // Use template specialization for C++98 when 'if constexpr' can't be
> > > > used.
> > > > +  template<bool _CanMemset>
> > > >      struct __uninitialized_fill_n
> > > >      {
> > > >        template<typename _ForwardIterator, typename _Size, typename _Tp>
> > > > @@ -319,47 +477,92 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > >    template<>
> > > >      struct __uninitialized_fill_n<true>
> > > >      {
> > > > +      // Overload for generic iterators.
> > > >        template<typename _ForwardIterator, typename _Size, typename _Tp>
> > > >         static _ForwardIterator
> > > >          __uninit_fill_n(_ForwardIterator __first, _Size __n,
> > > >                         const _Tp& __x)
> > > > -        { return std::fill_n(__first, __n, __x); }
> > > > +       {
> > > > +         if (__unwrappable_niter<_ForwardIterator>::__value)
> > > > +           {
> > > > +             _ForwardIterator __last = __first;
> > > > +             std::advance(__last, __n);
> > > > +             __uninitialized_fill<true>::__uninit_fill(__first, 
> > > > __last, __x);
> > > > +             return __last;
> > > > +           }
> > > > +         else
> > > > +           return std::__do_uninit_fill_n(__first, __n, __x);
> > > > +       }
> > > >      };
> > > > -
> > > > +#endif
> > > >    /// @endcond
> > > > 
> > > > +#pragma GCC diagnostic push
> > > > +#pragma GCC diagnostic ignored "-Wc++17-extensions"
> > > >     // _GLIBCXX_RESOLVE_LIB_DEFECTS
> > > >     // DR 1339. uninitialized_fill_n should return the end of its range
> > > >    /**
> > > >     *  @brief Copies the value x into the range [first,first+n).
> > > > -   *  @param  __first  An input iterator.
> > > > +   *  @param  __first  A forward iterator.
> > > >     *  @param  __n      The number of copies to make.
> > > >     *  @param  __x      The source value.
> > > > -   *  @return   Nothing.
> > > > +   *  @return   __first + __n.
> > > >     *
> > > > -   *  Like fill_n(), but does not require an initialized output range.
> > > > +   *  Like std::fill_n, but does not require an initialized output 
> > > > range.
> > > >    */
> > > >    template<typename _ForwardIterator, typename _Size, typename _Tp>
> > > >      inline _ForwardIterator
> > > >      uninitialized_fill_n(_ForwardIterator __first, _Size __n, const 
> > > > _Tp&
> > > > __x)
> > > >      {
> > > > +      // See uninitialized_fill conditions. We also require _Size to be
> > > > +      // an integer. The standard only requires _Size to be 
> > > > decrementable
> > > > +      // and contextually convertible to bool, so don't assume first+n
> > > > works.
> > > > +
> > > > +      // FIXME: We could additionally enable this for 1-byte enums.
> > > > +
> > > >        typedef typename iterator_traits<_ForwardIterator>::value_type
> > > >         _ValueType;
> > > > 
> > > > -      // Trivial types do not need a constructor to begin their 
> > > > lifetime,
> > > > -      // so try to use std::fill_n to benefit from its optimizations.
> > > > -      const bool __can_fill
> > > > -       = _GLIBCXX_USE_ASSIGN_FOR_INIT(_ValueType, const _Tp&)
> > > > -      // For arbitrary class types and floating point types we can't
> > > > assume
> > > > -      // that __n > 0 and std::__size_to_integer(__n) > 0 are 
> > > > equivalent,
> > > > -      // so only use std::fill_n when _Size is already an integral 
> > > > type.
> > > > -       && __is_integer<_Size>::__value;
> > > > +#if __cplusplus >= 201103L
> > > > +      if constexpr (__is_byte<_ValueType>::__value)
> > > > +       if constexpr (is_integral<_Tp>::value)
> > > > +         if constexpr (is_integral<_Size>::value)
> > > > +           {
> > > > +             using _BasePtr = decltype(std::__niter_base(__first));
> > > > +             if constexpr (is_pointer<_BasePtr>::value)
> > > > +               {
> > > > +                 void* __dest = std::__niter_base(__first);
> > > > +                 if (__builtin_expect(__n > 0, true))
> > > > +                   {
> > > > +                     __builtin_memset(__dest, (unsigned char)__x, __n);
> > > > +                     __first += __n;
> > > > +                   }
> > > > +                 return __first;
> > > > +               }
> > > > +#if __cpp_lib_concepts
> > > > +             else if constexpr (contiguous_iterator<_ForwardIterator>)
> > > > +               {
> > > > +                 auto __dest = std::__to_address(__first);
> > > > +                 if (__builtin_expect(__n > 0, true))
> > > > +                   {
> > > > +                     __builtin_memset(__dest, (unsigned char)__x, __n);
> > > > +                     __first += __n;
> > > > +                   }
> > > > +                 return __first;
> > > > +               }
> > > > +#endif
> > > > +           }
> > > > +      return std::__do_uninit_fill_n(__first, __n, __x);
> > > > +#else // C++98
> > > > +      const bool __can_memset = __is_byte<_ValueType>::__value
> > > > +                                 && __is_integer<_Tp>::__value
> > > > +                                 && __is_integer<_Size>::__value;
> > > > 
> > > > -      return __uninitialized_fill_n<__can_fill>::
> > > > +      return __uninitialized_fill_n<__can_memset>::
> > > >         __uninit_fill_n(__first, __n, __x);
> > > > +#endif
> > > >      }
> > > > -
> > > > -#undef _GLIBCXX_USE_ASSIGN_FOR_INIT
> > > > +#pragma GCC diagnostic pop
> > > > 
> > > >    /// @cond undocumented
> > > > 
> > > > @@ -619,7 +822,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > >             = std::__addressof(*__first);
> > > >           std::_Construct(__val);
> > > >           if (++__first != __last)
> > > > -           std::fill(__first, __last, *__val);
> > > > +           std::uninitialized_fill(__first, __last, *__val);
> > > >         }
> > > >      };
> > > > 
> > > > @@ -653,7 +856,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > >                 = std::__addressof(*__first);
> > > >               std::_Construct(__val);
> > > >               ++__first;
> > > > -             __first = std::fill_n(__first, __n - 1, *__val);
> > > > +             __first = std::uninitialized_fill_n(__first, __n - 1, 
> > > > *__val);
> > > 
> > > These last two changes seem to be missing in the ChangeLog.
> > 
> > Oops, yes. I'll take them out of this patch entirely. I have a
> > separate series of patches for those algos anyway. They should use
> > uninitialized_fill{,_n} instead of fill{,_n} for correctness, but the
> > conditions for deciding whether to use it need fixing anyway (we don't
> > care if it's assignable if we're not using std::fill).
> > 
> > I'll make the changes you suggested (except for a different fix for
> > the n==1 case, as described above) and push. Thanks for the detailed
> > review.
> > 
> > 

Reply via email to