关于Hinnant的堆栈分配器的问题 [英] Questions about Hinnant's stack allocator

查看:167
本文介绍了关于Hinnant的堆栈分配器的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在使用Howard Hinnant的堆栈分配器,它的工作方式类似于charm,但实现的一些细节


  1. 为什么全局运算符 new delete used? allocate() deallocate()成员函数使用 :: operator new :: operator delete 。类似地,成员函数 construct()使用全局布局new。为什么不允许任何用户定义的全局或类特定的重载?

  2. 为什么对齐设置为硬编码的16字节,而不是 std :: alignment_of< T> ; ? c> $ 异常规范?这不是不鼓励的(见例如More Effective C ++ Item 14.)?当分配器发生异常时,真的有必要终止和中止吗? c ++> c ++> c ++> c $ c>成员函数将是完美转发(对被调用的构造函数)的理想候选。

  3. 还需要什么其他修改才能使当前的代码与C ++ 11一致?

  4. ol>

    解决方案


    我一直在使用Howard Hinnant的 stack allocator ,它的工作原理
    像一个魅力,但一些细节的实现是有点
    我不清楚。


    很高兴为你工作。


    1。为什么使用全局运算符 new delete allocate() deallocate()成员函数使用 :: operator new
    :: operator delete 。类似地,成员函数
    construct()使用全局布局new。为什么不允许任何
    用户定义的全局或类特定的重载?


    没有特殊原因。随意修改这个代码以任何方式最适合你。这意味着更多的例子,它绝不是完美的。唯一的要求是,allocator和deallocator提供正确对齐的内存,并且构造成员构造一个参数。



    在C ++ 11中,成员是可选的。如果您在提供 allocator_traits 的环境中运行,我建议您从分配器中删除它们。要找出,只需删除它们,看看是否仍然编译。


    2。为什么对齐设置为硬编码的16个字节,而不是 std :: alignment_of< T>


    std :: alignment_of< T> 可能会正常工作。那天我可能是偏执的。


    3。为什么构造函数和 max_size 有一个 throw()异常规范?这不是不鼓励(见例如更有效的C ++
    项目14.)?当分配器中发生
    异常时,真的有必要终止和中止吗?这是否会随着新的C ++ 11
    noexcept 关键字而改变?


    这些成员不会永远扔。对于C ++ 11,我应该更新到 noexcept 。在C ++ 11中,使用 noexcept 来装饰事物变得更加重要,尤其是特殊成员。在C ++ 11中,可以检测表达式是否不是。代码可以根据这个答案分支。已知为nothrow的代码更可能导致通用代码分支到更有效的路径。 std :: move_if_noexcept 是C ++ 11中的规范示例。



    不要使用 throw(type1,type2) ever。它已在C ++ 11中弃用。



    当你真的想说: throw()这永远不会抛出,如果我错了,终止程序,所以我可以调试它。 throw()在C ++ 11中也被弃用,但是有一个插入替换: noexcept p>


    4。成员函数将成为完美转发(被调用的构造函数)的理想候选者。 construct()这是
    是写C ++ 11一致分配器的方法吗?


    是的。但是 allocator_traits 会为你做。让它。 std :: lib已经为你调试了这个代码。 C ++ 11容器将调用 allocator_traits< YourAllocator> :: construct(your_allocator,pointer,args ...)。如果你的分配器实现了这些函数,allocator_traits将调用你的实现,否则调用一个调试,高效的默认实现。


    5。还需要什么其他修改才能使当前代码C ++ 11符合?


    告诉你的事实,这个分配器不是真正的C ++ 03或C ++ 11一致。当复制分配器时,原始和副本应该彼此相等。在这个设计中,这是永远不是真的。然而,这个东西仍然只是碰巧在许多上下文中工作。



    如果你想使它严格符合,你需要另一个间接级别,使副本指向同一

    除了C ++ 11的分配器,C ++ 11分配器比C ++ 98/03分配器更容易构建。以下是必须执行的最低限度:

     模板< class T& 
    class MyAllocator
    {
    public:
    typedef T value_type;

    MyAllocator()noexcept; // only required if used
    MyAllocator(const MyAllocator&)noexcept; // copies must be equal
    MyAllocator(MyAllocator&&)noexcept; //如果copy ctor足够好就不需要
    template< class U>
    MyAllocator(const MyAllocator< U>& u)noexcept; // requires:* this == MyAllocator(u)

    value_type * allocate(std :: size_t);
    void deallocate(value_type *,std :: size_t)noexcept;
    };

    template< class T,class U>
    bool operator ==(const MyAllocator< T& const; MyAllocator< U>>)noexcept;

    template< class T,class U>
    bool operator!=(const MyAllocator< T& const; MyAllocator< U>>)noexcept;

    您可以选择考虑使用 MyAllocator 将以下嵌套类型放在分配器中:

      typedef std :: true_type propagate_on_container_swap; 

    还有一些其他的旋钮,你可以调整C ++ 11分配器。



    上面我注意到了我的堆栈分配器不符合,因为副本不相等的事实。我决定更新这个分配器到一个符合的C ++ 11分配器。新的分配器称为 short_allocator ,并记录在此处



    short_allocator 不同于堆栈分配器,因为内部缓冲区不再是分配器内部的,而是现在是一个单独的竞技场对象,可以位于本地堆栈,或给定线程或静态存储持续时间。 arena 不是线程安全的,所以小心这一点。你可以让它线程安全,如果你想,但是有递减的回报(最终你会重塑malloc)。



    这是符合,因为分配器的副本都指向相同的外部 arena 。请注意, N 的单位现在是字节,而不是 T 的数字。



    可以通过添加C ++ 98/03模板(typedefs,构造成员,销毁成员等)将这个C ++ 11分配器转换为C ++ 98/03分配器。 )。一个繁琐但简单的任务。



    这个问题的答案为新的 short_allocator 保持不变。


    I've been using Howard Hinnant's stack allocator and it works like a charm, but some details of the implementation are a little unclear to me.

    1. Why are global operators new and delete used? The allocate() and deallocate() member functions use ::operator new and ::operator delete respectively. Similarly, the member function construct() uses the global placement new. Why not allow for any user-defined global or class-specific overloads?
    2. Why is alignment set to hard-coded 16 bytes instead of std::alignment_of<T>?
    3. Why do the constructors and max_size have a throw() exception specification? Isn't this discouraged (see e.g. More Effective C++ Item 14.)? Is it really necessary to terminate and abort when an exception occurs in the allocator? Does this change with the new C++11 noexcept keyword?
    4. The construct() member function would be an ideal candidate for perfect forwarding (to the constructor that is being called). Is this the way to write C++11 conformant allocators?
    5. What other changes are necessary to make the current code C++11 conformant?

    解决方案

    I've been using Howard Hinnant's stack allocator and it works like a charm, but some details of the implementation are a little unclear to me.

    Glad it's been working for you.

    1. Why are global operators new and delete used? The allocate() and deallocate() member functions use ::operator new and ::operator delete respectively. Similarly, the member function construct() uses the global placement new. Why not allow for any user-defined global or class-specific overloads?

    There's no particular reason. Feel free to modify this code in whatever way works best for you. This was meant to be more of an example, and it is by no means perfect. The only requirements are that the allocator and deallocator supply properly aligned memory, and that the construct member constructs an argument.

    In C++11, the construct (and destroy) members are optional. I would encourage you to remove them from the allocator if you're operating in an environment that supplies allocator_traits. To find out, just remove them and see if things still compile.

    2. Why is alignment set to hard-coded 16 bytes instead of std::alignment_of<T>?

    std::alignment_of<T> would probably work fine. I was probably being paranoid that day.

    3. Why do the constructors and max_size have a throw() exception specification? Isn't this discouraged (see e.g. More Effective C++ Item 14.)? Is it really necessary to terminate and abort when an exception occurs in the allocator? Does this change with the new C++11 noexcept keyword?

    These members just won't ever throw. For C++11 I should update them to noexcept. In C++11 it becomes more important to decorate things with noexcept, especially special members. In C++11 one can detect whether an expression is nothrow or not. Code can branch depending on that answer. Code that is known to be nothrow is more likely to cause generic code to branch to a more efficient path. std::move_if_noexcept is the canonical example in C++11.

    Don't use throw(type1, type2) ever. It has been deprecated in C++11.

    Do use throw() when you really want to say: This will never throw, and if I'm wrong, terminate the program so I can debug it. throw() is also deprecated in C++11, but has a drop-in replacement: noexcept.

    4. The construct() member function would be an ideal candidate for perfect forwarding (to the constructor that is being called). Is this the way to write C++11 conformant allocators?

    Yes. However allocator_traits will do it for you. Let it. The std::lib has already debugged that code for you. C++11 containers will call allocator_traits<YourAllocator>::construct(your_allocator, pointer, args...). If your allocator implements these functions, allocator_traits will call your implementation, else it calls a debugged, efficient, default implementation.

    5. What other changes are necessary to make the current code C++11 conformant?

    To tell you the truth, this allocator isn't really C++03 or C++11 conformant. When you copy an allocator, the original and the copy are supposed to be equal to each other. In this design, that is never true. However this thing still just happens to work in many contexts.

    If you want to make it strictly conforming, you need another level of indirection such that copies will point to the same buffer.

    Aside from that, C++11 allocators are so much easier to build than C++98/03 allocators. Here's the minimum you must do:

    template <class T>
    class MyAllocator
    {
    public:
        typedef T value_type;
    
        MyAllocator() noexcept;  // only required if used
        MyAllocator(const MyAllocator&) noexcept;  // copies must be equal
        MyAllocator(MyAllocator&&) noexcept;  // not needed if copy ctor is good enough
        template <class U>
            MyAllocator(const MyAllocator<U>& u) noexcept;  // requires: *this == MyAllocator(u)
    
        value_type* allocate(std::size_t);
        void deallocate(value_type*, std::size_t) noexcept;
    };
    
    template <class T, class U>
    bool operator==(const MyAllocator<T>&, const MyAllocator<U>&) noexcept;
    
    template <class T, class U>
    bool operator!=(const MyAllocator<T>&, const MyAllocator<U>&) noexcept;
    

    You might optionally consider making MyAllocator Swappable and put the following nested type in the allocator:

    typedef std::true_type propagate_on_container_swap;
    

    There's a few other knobs like that you can tweak on C++11 allocators. But all of the knobs have reasonable defaults.

    Update

    Above I note that my stack allocator is not conforming due to the fact that copies are not equal. I've decided to update this allocator to a conforming C++11 allocator. The new allocator is called short_allocator and is documented here.

    The short_allocator differs from the stack allocator in that the "internal" buffer is no longer internal to the allocator, but is now a separate "arena" object that can be located on the local stack, or given thread or static storage duration. The arena isn't thread safe though so watch out for that. You could make it thread safe if you wanted to, but that has diminishing returns (eventually you'll reinvent malloc).

    This is conforming because copies of allocators all point to the same external arena. Note that the unit of N is now bytes, not number of T.

    One could convert this C++11 allocator to a C++98/03 allocator by adding the C++98/03 boiler-plate (the typedefs, the construct member, the destroy member, etc.). A tedious, but straightforward task.

    The answers to this question for the new short_allocator remain unchanged.

    这篇关于关于Hinnant的堆栈分配器的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆