Amxx
Amxx

Reputation: 3070

initialize an array of non trivialy constructible objects

I have a class Foo that operates that need a reference to be built (it will be used to work on that memory space)

template<typename T>
class Foo {
  public:
    Foo(T& value) : _value(value) {}
    ...
  private:
    T& _value;
};

I also have a linear memory space of multiple instances of T

std::array<T, SIZE> buffer;

What I'd like to build is an array of object of type Foo which maps the differents instances of my buffer. Which means each instance of Foo has to be built using the correct reference.

std::array<Foo<T>, SIZE> operators;

Still, operators is cannot be trivially initialize, and I can manage to build it by 'mapping' the buffer through the 'Foo' constructor.

Is there any way to do ? I tried using std::forward and std::initializer_list but those cannot be constructed from my buffer.

Note that I need my buffer to stay aligned for communication purposes, and I will en up overloading the Foo class to implement different behaviour for different elements of my array.

Upvotes: 3

Views: 117

Answers (4)

HelloWorld
HelloWorld

Reputation: 1863

std::array must know on initialization how to construct the references. Prefer using a pointer like this:

template<typename T>
class Foo {
  public:
    Foo() { } // used by std::array to create Foo<T>
    Foo(T& value) : _value(&value) {}
    operator T&() { return *_value; } // never call this on uninitialised objects
  private:
    T* _value = nullptr;
};

What you can do is to create a static object to pre-initialze your reference.

template<typename T>
class Foo {
  static T _default; // default-object used to initialize the reference
  public:
    Foo() { } // used by std::array to create Foo<T>
    Foo(T& value) : _value(value) {}

    T& _value = _default;
};

int a = 0;
std::array<Foo<int>, 10> arr;
arr[0]._value = a;

Upvotes: 2

Vaughn Cato
Vaughn Cato

Reputation: 64308

Here's an example of how to use the indices trick to build your array:

#include <array>
#include <iostream>

template<typename T>
class Foo {
    public:
        Foo(T& value) : _value(value) {}
        ...
    private:
        T& _value;
};

// Typical varadic indices builder
template<size_t... Is> struct Indices { };

template <size_t count,size_t... Is>
struct BuildIndices : BuildIndices<count-1,count-1,Is...> { };

template<size_t... Is>
struct BuildIndices<0,Is...> : Indices<Is...> { };


// Helper function
template <typename T,size_t... Is>
static std::array<Foo<T>,sizeof...(Is)>
    make_foo_array_with_indices(
            std::array<T,sizeof...(Is)> &values,
            Indices<Is...>
            )
{
    return {{values[Is]...}};
}

template <typename T,size_t count>
static std::array<Foo<T>,count>
    make_foo_array(std::array<T,count> &values)
{
    return make_foo_array_with_indices(values,BuildIndices<count>());
}


int main()
{
    std::array<int,4> buffer = {3,1,4,1};
    std::array<Foo<int>,4> operators = make_foo_array(buffer);
}

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409166

Unfortunately there is no way to directly initialize the operators array.

The only solution I can think of is to make Foo default constructible, and then loop over buffer (using a loop or e.g. std::transform) and "initialize" each entry in operators using assignment.

Something like

template<typename T, std::size_t N>
void initialize_operators(const std::array<T, N>& buff,
                          std::array<Foo<T>, N>& ops)
{
    std::transform(std::begin(buf), std::end(buff), std::begin(ops),
                   [](const T& t) { return Foo<T>(t); });
}

Upvotes: 1

Kerrek SB
Kerrek SB

Reputation: 477000

One way is to spell out the initializer elements:

T a, b;

std::array<Foo<T>, 5> a = { {
    Foo<T>(a), Foo<T>(b), Foo<T>(a), Foo<T>(b), Foo<T>(a) } };

Another way would be to separate storage from object construction and manage the array element lifetimes manually.

Upvotes: 0

Related Questions