首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++17 allocator_traits实现

C++17 allocator_traits实现
EN

Code Review用户
提问于 2019-05-12 01:45:53
回答 1查看 685关注 0票数 5

在前面的问题C++17pointer_traits实现的启发下,我重新实现了名为my_std::allocator_traitsallocator_traits,并放入了一个单独的标题allocator_traits.hpp,因为<memory>太全面了:

代码语言:javascript
复制
// C++17 allocator_traits implementation

#ifndef INC_ALLOCATOR_TRAITS_HPP_D6XSISB6AD
#define INC_ALLOCATOR_TRAITS_HPP_D6XSISB6AD

#include <limits>      // for std::numeric_limits
#include <memory>      // for std::pointer_traits
#include <type_traits> // for std::false_type, etc.
#include <utility>     // for std::forward

namespace my_std {

  template <class Alloc>
  struct allocator_traits;

  namespace at_detail {

    template <class Tmpl, class U>
    struct rebind_first_param { };
    template <template <class, class...> class Tmpl, class T, class... Args, class U>
    struct rebind_first_param<Tmpl<T, Args...>, U> {
      using type = Tmpl<U, Args...>;
    };
    template <class Tmpl, class U>
    using rebind_first_param_t = typename rebind_first_param<Tmpl, U>::type;

    template <class Alloc, class T>
    auto pointer(int) -> typename Alloc::pointer;
    template <class Alloc, class T>
    auto pointer(long) -> T*;

    template <class Alloc, class T, class Ptr>
    auto const_pointer(int) -> typename Alloc::const_pointer;
    template <class Alloc, class T, class Ptr>
    auto const_pointer(long) -> typename std::pointer_traits<Ptr>::template rebind<const T>;

    template <class Alloc, class Ptr>
    auto void_pointer(int) -> typename Alloc::void_pointer;
    template <class Alloc, class Ptr>
    auto void_pointer(long) -> typename std::pointer_traits<Ptr>::template rebind<void>;

    template <class Alloc, class Ptr>
    auto const_void_pointer(int) -> typename Alloc::const_void_pointer;
    template <class Alloc, class Ptr>
    auto const_void_pointer(long) -> typename std::pointer_traits<Ptr>::template rebind<const void>;

    template <class Alloc, class Ptr>
    auto difference_type(int) -> typename Alloc::difference_type;
    template <class Alloc, class Ptr>
    auto difference_type(long) -> typename std::pointer_traits<Ptr>::difference_type;

    template <class Alloc, class Diff>
    auto size_type(int) -> typename Alloc::size_type;
    template <class Alloc, class Diff>
    auto size_type(long) -> std::make_unsigned_t<Diff>;

    template <class Alloc>
    auto pocca(int) -> typename Alloc::propagate_on_container_copy_assignment;
    template <class Alloc>
    auto pocca(long) -> std::false_type;

    template <class Alloc>
    auto pocma(int) -> typename Alloc::propagate_on_container_move_assignment;
    template <class Alloc>
    auto pocma(long) -> std::false_type;

    template <class Alloc>
    auto pocw(int) -> typename Alloc::propagate_on_container_swap;
    template <class Alloc>
    auto pocw(long) -> std::false_type;

    template <class Alloc>
    auto iae(int) -> typename Alloc::is_always_equal;
    template <class Alloc>
    auto iae(long) -> std::is_empty<Alloc>::type;

    template <class Alloc, class T>
    auto rebind_alloc(int) -> typename Alloc::rebind<T>::other;
    template <class Alloc, class T>
    auto rebind_alloc(long) -> rebind_first_param_t<Alloc, T>;

  }

  template <class Alloc>
  struct allocator_traits {
    using allocator_type = Alloc;
    using value_type = typename Alloc::value_type;

    using pointer = decltype(at_detail::pointer<Alloc, value_type>(0));
    using const_pointer = decltype(at_detail::const_pointer<Alloc, value_type, pointer>(0));
    using void_pointer = decltype(at_detail::void_pointer<Alloc, pointer>(0));
    using const_void_pointer = decltype(at_detail::const_void_pointer<Alloc, pointer>(0));

    using difference_type = decltype(at_detail::difference_type<Alloc, pointer>(0));
    using size_type = decltype(at_detail::size_type<Alloc, difference_type>(0));

    using propagate_on_container_copy_assignment = decltype(at_detail::pocca<Alloc>(0));
    using propagate_on_container_move_assignment = decltype(at_detail::pocma<Alloc>(0));
    using propagate_on_container_swap = decltype(at_detail::pocw<Alloc>(0));
    using is_always_equal = decltype(at_detail::iae<Alloc>(0));

    template <class T>
    using rebind_alloc = decltype(at_detail::rebind_alloc<Alloc, T>(0));

    static pointer allocate(Alloc& a, size_type n)
    {
      return a.allocate(n);
    }
    static pointer allocate(Alloc& a, size_type n, const_void_pointer hint)
    {
      return allocate_(a, n, hint, 0);
    }

    static void deallocate(Alloc& a, pointer p, size_type n)
    {
      a.deallocate(p, n);
    }

    template <class T, class... Args>
    static void construct(Alloc& a, T* p, Args&&... args)
    {
      construct_(a, p, 0, std::forward<Args>(args)...);
    }

    template <class T>
    static void destroy(Alloc& a, T* p)
    {
      destroy_(a, p, 0);
    }

    static size_type max_size(const Alloc& a) noexcept
    {
      return max_size_(a, 0);
    }

    static Alloc select_on_container_copy_construction(const Alloc& rhs)
    {
      return soccc(rhs, 0);
    }

  private:
    static auto allocate_(Alloc& a, size_type n, const_void_pointer hint, int)
      -> decltype(a.allocate(n, hint), void(), std::declval<pointer>())
    {
      return a.allocate(n, hint);
    }
    static auto allocate_(Alloc& a, size_type n, const_void_pointer, long)
      -> pointer
    {
      return a.allocate(n);
    }

    template <class T, class... Args>
    static auto construct_(Alloc& a, T* p, int, Args&&... args)
      -> decltype(a.construct(p, std::forward<Args>(args)...), void())
    {
      a.construct(p, std::forward<Args>(args)...);
    }
    template <class T, class... Args>
    static void construct_(Alloc&, T* p, long, Args&&... args)
    {
      ::new(static_cast<void*>(p)) T(std::forward<Args>(args)...);
    }

    template <class T>
    static auto destroy_(Alloc& a, T* p, int)
      -> decltype(a.destroy(p), void())
    {
      a.destroy(p);
    }
    template <class T>
    static void destroy_(Alloc&, T* p, long)
    {
      p->~T();
    }

    static auto max_size_(const Alloc& a, int) noexcept
      -> decltype(a.max_size(), std::declval<size_type>())
    {
      return a.max_size();
    }
    static auto max_size_(const Alloc&, long) noexcept
      -> size_type
    {
      return std::numeric_limits<size_type>::max() / sizeof(value_type);
    }

    static auto soccc(const Alloc& rhs, int)
      -> decltype(rhs.select_on_container_copy_construction(), std::declval<Alloc>())
    {
      return rhs.select_on_container_copy_construction();
    }
    static auto soccc(const Alloc& rhs, long)
      -> Alloc
    {
      return rhs;
    }
  };

}

#endif

我使用N4659作为参考。

EN

回答 1

Code Review用户

回答已采纳

发布于 2019-05-13 11:30:40

嗯,看起来很干净,很好,很好。

  1. 当然,如果它真的是实现的一部分,它将不得不使用单独的保留标识符,以避免与奇怪和不明智的用户定义宏交互,从而使它看起来不太好。
  2. Tmpl是主类型模板参数的奇怪名称。请保持习惯的T,除非你有一个更有说服力的名字,如Alloc
  3. Tmpl也是模板模板参数的奇怪名称。TT是习惯的,更简洁。
  4. 如果你不需要名字的话,可以考虑把名字去掉,它们也不会把有用的额外信息传递给读者。
  5. 我想知道您用哪种逻辑来决定是将某个东西作为私有成员放置,还是在私有名称空间中进行实现-细节。虽然这两者都有很好的理由,但最好只使用一个。
  6. 一个真正的实现可能会以某种实现定义的方式将ODR使用的内部函数标记为always_inline。
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/220113

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档