Как инициализировать последовательность неподвижных, не копируемых объектов?

Допустим, у меня есть тип, который не является ни подвижным, ни копируемым:

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

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

Теперь, учитывая число, известное во время компиляции (назовите его N), есть ли способ, которым я могу создать «последовательность» из них в стеке, каждый из которых инициализируется числами от 0 до N-1? Я был бы доволен массивом в стиле C foo[N], std::array< foo, N >или, может быть, даже std::tuple какой-то

Чего я пытаюсь избежать, так это писать:

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

когда мне кажется, что это то, что компилятор должен сделать для меня. Лучшее, что я смог придумать, — это использовать boost::optional,

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

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

Но это зависит от логики времени выполнения, даже если вся необходимая информация доступна во время компиляции. Кроме того, я остаюсь с чем-то, что ведет себя как массив указателей.

5

Решение

// 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();

Если это кажется много хлопот, это так. Но вы можете легко инкапсулировать эту функциональность в классе.

3

Другие решения

Хотя это не совсем массив, вы можете сделать это с помощью рекурсии шаблона.

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

Использование:

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
1

Как ответ от Бенджамина Линдли, но упакованный в классе:

#include <type_traits>
#include <utility>
#include <new>

template<typename T>
class uninitialized {
public:
constexpr uninitialized() { }

~uninitialized() {
get().~T();
}

explicit uninitialized(const uninitialized& other) {
construct(other);
}

explicit uninitialized(uninitialized&& other) {
construct(std::move(other));
}

template<class... Args>
explicit uninitialized(Args&&... args) {
construct(std::forward<Args>(args)...);
}

template<class... Args>
void construct(Args&&... args) noexcept {
static_assert(std::is_nothrow_constructible<T, Args...>::value, "constructor should not throw!");
::new(getPointer()) T (std::forward<Args>(args)...);
}

uninitialized& operator = (const T& t) {
get() = t;
return *this;
}

uninitialized& operator = (T&& t) {
get() = std::move(t);
return *this;
}

T* operator -> () { return getPointer(); }
T& operator * () { return get(); }
T* operator & () { return getPointer(); }
T* getPointer() { return reinterpret_cast<T*>(&data); }
T& get() { return *reinterpret_cast<T*>(&data); }

const T* operator -> () const { return getPointer(); }
const T& operator * () const { return get(); }
const T* operator & () const { return getPointer(); }
const T* getPointer() const { return reinterpret_cast<const T*>(&data); }
const T& get() const { return *reinterpret_cast<const T*>(&data); }

private:
std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type data;
};

Теперь все немного проще:

uninitialized<foo> f[N];

for (size_t i = 0; i < N; ++i)
f[i].construct(i);

for (const auto& fooref : f)
fooref->bar();

// foo::~foo is called for you
1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector