没有默认构造函数的动态数组创建

Dynamic array creation without default constructor

免责声明:我已经知道原始数组不是 C++ 中的第一个 class 元素,而且在许多地方,我们希望用向量替换它们。但我还是希望换一种方式...

上下文:

我正在尝试使用连续数据而不是向量的向量来构建多维容器库。一个简单的类比是二维 C 数组与指针数组。多维部分作为一维数组上的代理 classes 处理。这意味着使用 代理迭代器 但我当前的问题是底层 1D 数组的处理。

理想情况下,一维数组可以使用静态或自动内存(并且不应在使用后释放)或应该使用的私有动态内存。到目前为止一切顺利,我目前使用布尔值 (owning) 来记住是否应删除数组。我需要 手动管理该内存,因为我希望子数组重新使用其容器的内存并且不能使用标准容器的复制语义。

问题:

如果底层数据类型有一个默认构造函数,一切都很好,我可以安全地用 new[] 构建一个动态数组,然后用 delete[] 释放它。但我也想知道是否可以只需要一个可复制类型,并使用默认值初始化数组(std::vector 可以做什么)。

问题:

如何构建非默认可构造对象的动态数组,而不是使用默认值的复制初始化?

当前研究:

如果默认构造函数可用于 class T,我可以写:

    template <typename T>
    class Holder
    {
        size_t sz;         // size of the array
        bool owning;       // true if the array should be deleted
        T* data;           // pointer to the underlying array

    public:
        // builds an array of default initialized data
        Holder(size_t sz) : sz(sz), owning(true) {
            data = new T[sz];
        }

        // uses an existing array. Do not own it by default but can steal
        // it if own is true
        Holder(T* data, size_t sz, bool own=false) : sz(sz),
            owning(own), data(data) {};

        // builds an array of data initialized from a default value
        Holder(const T&value, size_t sz) : sz(sz), owning(true) {
            // what can be done here if no default ctor is available?
        }

        ~Holder() {
            if (owning) {
                delete[] data;
            }
        }

        // Copy ctor. Will borrow the original array
        Holder(const Holder<T>& other) : sz(other.sz), owning(false), data(other.data){}

        // Move ctor
        Holder(Holder&& other) : sz(other.sz), owning(other.owning) {
            data = other.data;
            other.data = nullptr;
            other.owning = false;
        }
        // assignment operators and accessors omitted for brievety
        ...
    };

我也知道我可以安全地用 std::uninitialized_fill 填充一个单元化数组,然后用 std::destroy 销毁它的对象,但我不确定如何正确使用它以及它是否与new[]delete[]。换句话说,我可以 delete 一个用 operator new[] 分配并用 uninitialized_fill 初始化的数组吗?

I am unsure of how to correctly use that and whether it is compatible with new[] and delete[].

new T[]不兼容,但与new char[]兼容。

template <typename T>
class Holder
{
    size_t sz;         // size of the array
    bool owning;       // true if the array should be deleted
    T* data;           // pointer to the underlying array

    using storage_t = std::aligned_storage_t<sizeof(T), alignof(T)>;

    static T* alloc(size_t sz) { // or just use an Allocator
        return reinterpret_cast<T*>(reinterpret_cast<char*>(new storage_t[sz])); 
    }

public:
    // builds an array of default initialized data
    Holder(size_t sz) requires std::default_initializable<T> : sz(sz), owning(true), data(alloc(sz)) {
        std::uninitialized_default_construct_n(data, sz);
    }

    // uses an existing array. Do not own it by default but can steal
    // it if own is true
    // This is now suspect, we should take a deleter here.
    // Holder(T* data, size_t sz, bool own=false) : sz(sz),
    //     owning(own), data(data) {};

    // uses an existing array. Do not own it
    Holder(T* data, size_t sz) : sz(sz), owning(false), data(data) {};

    // uses an existing array. Do not own it
    template <size_t N>
    Holder(T (&data)[N]) : sz(N), owning(false), data(data) {};

    // builds an array of data initialized from a default value
    Holder(const T&value, size_t sz) requires std::copy_constructible<T> : sz(sz), owning(true), data(alloc(sz)) {
        std::uninitialized_fill_n(data, sz, value);
    }

    ~Holder() {
        if (owning) {
            std::destroy_n(data, sz);
            delete[] reinterpret_cast<storage_t*>(data);
        }
    }

    // Copy ctor. Will borrow the original array
    Holder(const Holder<T>& other) : sz(other.sz), owning(false), data(other.data){}

    // Move ctor
    Holder(Holder&& other) : data(std::exchange(other.data, nullptr)) sz(other.sz), owning(std::exchange(other.owning, false)) {
    }
    // assignment operators and accessors omitted for brievety
    ...
};