如何初始化大尺寸的std :: array

时间:2020-04-16 23:20:08

标签: c++

我有一个类,该类包含较大的std :: array,如何初始化数组?

请参阅课程测试;

样本:

class context{......}

class value
{
public:
    value(context& ctx) : ctx_(ctx){
    }

protected:
    context& ctx_;

    int data_ = 0;
}


class test
{
public:
    test() : /*i need to initialize values at here*/ values_{ctx_,.....}
    {
    }

protected:
    context ctx_;
    std::array<value_t,10000> values_;
}

实际上,这个数组可能只包含3或5个元素,而不是10000,但是有些人肯定会给我下面的答案

test() : values_{ctx_,ctx_,ctx_,ctx_,ctx_}
{
}

我不需要像上面那样尴尬的答案。

有没有一种方法可以使用简单的代码(如折叠表达式)来初始化std :: array?

3 个答案:

答案 0 :(得分:1)

您可以委托给带有参数包的构造函数,然后将其折叠:

#include <utility>
#include <cstddef>

class test
{
public:
    test() : test(std::make_index_sequence<10000>{}) {}
private:
    template<std::size_t... I>
    test(std::index_sequence<I...>) : values_{{(I, ctx_)...}} {}
protected:
    context ctx_;
    std::array<value_t, 10000> values_;
};

尽管对于我来说,在-O0以外的任何优化级别上,这绝对是 killed 的编译时间(并且可能会破坏您编译的代码大小)

您也可以尝试构造到未初始化的内存中,因此您不需要默认构造:

#include <array>
#include <cstddef>
#include <new>
#include <memory>

class test
{
public:
    test() {
        std::byte* p = value_memory_;
        for (std::byte* end = std::end(value_memory_); p < end; p += sizeof(value_t)) {
            new (p) value_t(ctx_);
        }
    }

    ~test() {
        value_t* values = get_values();
        std::destroy(values, values + 10000);
    }
protected:
    context ctx_;

    value_t* get_values() {
        return std::launder(reinterpret_cast<value_t*>(value_memory_));
    }
    const value_t* get_values() const {
        return std::launder(reinterpret_cast<const value_t*>(value_memory_));
    }

    // These are UB, but work on most compilers, and would generally be nicer
    // to work with
    value_t(&get_values())[10000] {
        return *std::launder(reinterpret_cast<value_t(*)[10000]>(value_memory_));
    }
    const value_t(&get_values() const)[10000] {
        return *std::launder(reinterpret_cast<const value_t(*)[10000]>(value_memory_));
    }
private:
    alignas(value_t) std::byte value_memory_[sizeof(value_t) * 10000u];
};

这将花费一些运行时成本,并且您必须丢掉std::array(除非您要使用std::array<std::aligned_storage_t<sizeof(value_t), alignof(value_t)>, 10000>,否则必须清洗数组的每个元素)

答案 1 :(得分:1)

问题是您的数组包含没有默认构造函数的类型的元素,因此当您声明拥有该类型的std::array时,只能使用aggregate initialization初始化该数组,因此您可以将值显式传递给每个元素的构造函数。当数组是类或结构的成员时,该初始化需要使用类/结构构造函数的member initialization list。正是您要避免的事情。

要解决此问题,可以使用placement-new显式地在循环中分别构造每个数组元素:

#include <type_traits>

class context{......}

class value
{
public:
    value(context& ctx) : ctx_(ctx){
    }

protected:
    context& ctx_;

    int data_ = 0;
}


class test
{
public:
    test()
    {
        for (auto &v : values_)
            new (&v) value(ctx_);
    }

    ~test()
    {
        for (auto &v : values_) {
            // note: needs std::launder in C++17 and later
            // std::launder(reinterpret_cast<value*>(&v))->~value();
            reinterpret_cast<value*>(&v)->~value();
        }
    }

protected:
    context ctx_;

    using storage_type = std::aligned_storage<sizeof(value), alignof(value)>::type;
    std::array<storage_type, 10000> values_;

    // Access an object in aligned storage
    value& operator[](std::size_t pos)
    {
        // note: needs std::launder in C++17 and later
        // return *std::launder(reinterpret_cast<value*>(&values_[pos]));
        return *reinterpret_cast<value*>(&values_[pos]);
    }
};

答案 2 :(得分:0)

您可以在数组上使用fill()方法: https://en.cppreference.com/w/cpp/container/array/fill