Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to initialize a sequence of non-movable, non-copyable objects?

Let's say I have a type which is neither movable nor copyable:

struct foo
{
  explicit foo( size_t ){}
  ~foo(){}

  foo( foo const & ) = delete;
  foo( foo && ) = delete;
  foo& operator=( foo const & ) = delete;
  foo& operator=( foo & ) = delete;
};

Now given a number known at compile time (call it N), is there any way that I can create a "sequence" of these on the stack with each one initialized with numbers 0 through N-1? I would be satisfied with a C-style array foo[N], a std::array< foo, N >, or perhaps even a std::tuple of some kind.

What I'm trying to avoid is writing out:

foo f0( 0 ), f1( 1 ), ... fNminus1( N-1 );

when it feels like this is something the compiler should be able to do for me. The best I've been able to come up with is using boost::optional.

boost::optional< foo > f[N];

for( size_t i = 0U; i < N; ++i )
  f[i] = boost::in_place( i );

But that relies on runtime logic even though all the required information is available at compile-time. Plus, I'm left with something that behaves like an array of pointers.

like image 617
Andrew Durward Avatar asked Apr 12 '13 03:04

Andrew Durward


2 Answers

// create a type with the proper alignment
typedef std::aligned_storage<sizeof(foo), std::alignment_of<foo>::value>::type buffer_type;

const int N = 10;
// create an array of uninitialized raw data
buffer_type storage_buffer[N];

// initialize each foo object with placement new
for (size_t i=0; i<N; ++i)
    new (storage_buffer + i) foo(i);

foo * fp = (foo*)(&storage_buffer);
// access your foo objects via fp


// you must manually call the destructor of each object
for (size_t i=0; i<N; ++i)
    fp[i].~foo();

If that seems like a lot of hassle, it is. But you could easily encapsulate that functionality in a class.

like image 95
Benjamin Lindley Avatar answered Sep 24 '22 08:09

Benjamin Lindley


Although not strictly an array, you can sort of accomplish this with template recursion

template< typename T, size_t N >
struct type_array : public type_array< T, N-1 > {
    // this is the Nth element
    T elem;
    // it is constructed with N
    type_array() : elem( N ) {}

    // member function to return the Nth element
    T & get( size_t n ) {
        if ( n == N ) {
            return elem;
        } else {
            return type_array< T, N-1 >::get( n );
        }
    }
};

// base case when N == 0
template< typename T >
struct type_array<T, 0> {
    T elem;
    type_array() : elem( 0 ) {}
    T & get( size_t n ) {
      return elem;
    }
};

Usage:

type_array< foo, 100 > foo_array;   // construct 100 foos
foo_array.get(1);                   // foo with n == 1
foo_array.get(2);                   // foo with n == 2
like image 35
yngccc Avatar answered Sep 23 '22 08:09

yngccc