c++ - secuencias - ¿Cómo inicializar una secuencia de objetos no movibles, no copiables?
secuencias sql ejemplos (3)
Digamos que tengo un tipo que no es ni movible ni se puede copiar:
struct foo
{
explicit foo( size_t ){}
~foo(){}
foo( foo const & ) = delete;
foo( foo && ) = delete;
foo& operator=( foo const & ) = delete;
foo& operator=( foo & ) = delete;
};
Ahora, dado un número conocido en tiempo de compilación (llámalo N), ¿hay alguna manera de que pueda crear una "secuencia" de estos en la pila con cada uno inicializado con los números 0 a N-1? Me sentiría satisfecho con una matriz de estilo C foo[N]
, una std::array< foo, N >
, o incluso una std::tuple
de algún tipo.
Lo que intento evitar es escribir:
foo f0( 0 ), f1( 1 ), ... fNminus1( N-1 );
cuando parece que esto es algo que el compilador debería poder hacer por mí. Lo mejor que he podido encontrar es usar boost::optional
.
boost::optional< foo > f[N];
for( size_t i = 0U; i < N; ++i )
f[i] = boost::in_place( i );
Pero eso depende de la lógica de tiempo de ejecución aunque toda la información requerida está disponible en tiempo de compilación. Además, me queda algo que se comporta como una serie de indicadores.
Aunque no es estrictamente una matriz, puede lograrlo con la recursión de la plantilla
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;
}
};
Uso:
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
// 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();
Si eso parece una gran molestia, lo es. Pero podrías encapsular fácilmente esa funcionalidad en una clase.
Como la respuesta de Benjamin Lindley, pero lleno en una clase:
#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;
};
Ahora las cosas son un poco más fáciles:
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