2

i have a class,the class contains a large size of std::array,how to initialize the array??

see class test;

sample:

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_;
}

in reality,this array maybe only contains 3 or 5 element,not 10000,but someof people definitely gonna give me an answer like below

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

i don't need a awkward answer like above.

is there a way to initialize std::array with simple code like fold expression???

3
  • Do you require each element of the array initialized because you need each element ready to use immediately? Or, can you deal with uninitialized values that can be initialized at a later time? Commented Apr 17, 2020 at 0:17
  • 1
    FWIW, if you don't mind a single memory allocation, auto values = std::vector<value_t>(ctx_, 10000); does what you want. Commented Apr 17, 2020 at 0:22
  • Related to c11-initialize-array-with-uniform-value-in-constexpr-function Commented Apr 17, 2020 at 6:12

3 Answers 3

1

You can delegate to a constructor that takes a parameter pack then fold over that:

#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_;
};

Though this absolutely killed compile time at any level of optimisation other than -O0 for me (And will probably blow up your compiled code size)

You could also try constructing into uninitialised memory so you don't need to default construct:

#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];
};

Which will have some runtime cost, and you have to lose the std::array (Unless you go for a std::array<std::aligned_storage_t<sizeof(value_t), alignof(value_t)>, 10000>, in which case you have to launder every single element of the array)

Sign up to request clarification or add additional context in comments.

Comments

1

The problem is that your array holds elements of a type that does not have a default constructor, so when you declare a std::array holding that type, the array can only be initialized using aggregate initialization so you can explicitly pass in a value to each element's constructor. When the array is a member of a class or struct, that initialization requires use of the class/struct constructor's member initialization list. Exactly what you are trying to avoid.

To get around this, you can use placement-new to explicitly construct each array element individually in a loop:

#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]);
    }
};

Comments

0

You can use fill() method on the array: https://en.cppreference.com/w/cpp/container/array/fill

1 Comment

The array in question needs to be initialized before it can be filled, but value does not have a default constructor, so the values_ array can't be initialized.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.