3

我有一个分配器适配器,我称之为ctor_allocator. 我写它更多是为了实验而不是为了任何实际需要。它使分配器在可行时使用默认构造,从而通过分配绕过任何默认初始化。例如,双精度向量的构造不会将值初始化为 0.0(或任何值)。

适配器中的许多代码都是旧式的,而且看起来可能很快就会被弃用,如果还没有的话。在不违背其目的的情况下,我还没有成功地对代码进行现代化改造。例如,如果我删除struct rebind模板,则零初始化返回。

你能展示如何对其进行现代化改造吗?

答案可能对其他应用程序具有指导意义。

编辑:在评论中,布兰登将这两个链接作为“新方式”的示例。SO问题最小示例。两者都不能防止使用零进行初始化,这发生在construct. 但是,如果我通过添加void construct(U* ptr)模板来修改任一示例,则可以避免零初始化。这回答了一个与我在这里提出的不同的问题,但它是一个好问题的好答案。

#include <memory>   
namespace dj {
    template <typename T, typename A = std::allocator<T>>
    class ctor_allocator : public A
    {
        using a_t = std::allocator_traits<A>;
    public:
        using A::A; // Inherit constructors from A
        template <typename U>
        struct rebind {
            using other  = ctor_allocator<U, typename a_t::template rebind_alloc<U>>;
        };

        template <typename U>
        void construct(U* ptr)
            noexcept(std::is_nothrow_default_constructible<U>::value)
        {
            ::new(static_cast<void*>(ptr)) U;
        }

        template <typename U, typename...Args>
        void construct(U* ptr, Args&&... args) {
            a_t::construct(static_cast<A&>(*this),
                ptr, std::forward<Args>(args)...);
        }
    };
}

// Test case. main.cpp
#include <iostream>
#include <vector>

template<class T>
using vector = std::vector<T, dj::ctor_allocator<T>>;

int main() {
    {
        vector<int> v(10);
        for (int i = 0; i < 10; ++i) {
            v[i] = i * 56313;
        }
    }
    // If ctor_allocator works as intended, 
    // this probably will not print all zeros.
    vector<int> v(10);
    for (int i = 0; i < 20; ++i) {
        std::cout << std::hex << v[i] << " ";
    }
    std::cout << std::endl;
}
4

1 回答 1

3

多亏了布兰登的提示,我才弄明白了——至少大部分是这样。一个可能的解决方案是委托给基类,而不是从它继承allocatedeallocate至少在 VC++2017 上,我仍然需要专业化construct,尽管我已经看到文档表明我提供的专业化正是默认值。

以下可能需要一些复制构造函数和赋值运算符的东西。请指教。

#include <memory>

namespace dj {  

template<typename T, typename Base = std::allocator<T>>
struct ctor_allocator 
    {           
        Base A;
        using value_type = typename Base::value_type;
        using pointer = typename Base::pointer;

        pointer allocate(std::size_t n, const void* hint = 0)
        {
            auto ret = A.allocate(n,hint);
            return ret;
        }

        void deallocate(pointer ptr, std::size_t n)
        {
            A.deallocate(ptr, n);
        }

        template <typename U >
        void construct(U* ptr) {
                ::new (ptr) U;
        }   
    };

template <typename T, typename U>
    inline 
    bool operator==(const ctor_allocator<T>& left, const ctor_allocator<U>& right) 
        {
            return left.A == right.A;
        }

template <typename T, typename U>
    inline bool operator != (const ctor_allocator<T>& a, const ctor_allocator<U>& b) 
        {
            return !(a == b);
        }
}

// MAIN.cpp
#include <vector>
template<class T>
using vector = std::vector<T, dj::ctor_allocator<T>>;

int main() {
    {
        vector<int> v(10);
        for (int i = 0; i < 10; ++i) {
        v[i] = i * 313;
        }
    }
    vector<int> v(20);
    for (auto i: v) {
        std::cout << std::hex << i << " ";
    }
    std::cout << std::endl;
}
于 2018-02-15T23:45:16.310 回答