I just ran across some unexpected and frustrating behaviour while working on a C++ project. My actual code is a tad more complicated, but the following example captures it just
Tried just creating the array with a size? Default ctor should be called.
As in this source
struct foo {
int x;
foo():x(1) {}
private:
foo( foo const& ) {}
};
foo array[10];
#include
int main() {
for (auto&& i:array) {
std::cout << i.x << "\n";
}
}
which demonstrates initialized foo
in an array with no default copy constructor.
If your problem is that you actually want to construct the foo
with a non-default constructor, this can be done as well, but it is much harder, and that isn't what your question asked. In any case, here is a very, very rough sketch of the kind of stuff needed to create an array-like structure that supports emplaced construction of its elements. It is far from finished or compiling, but the basic technique should be sound:
#include
#include
#include
template
struct types {};
template
struct emplacer;
template
struct remove_refref {
typedef T type;
};
template
struct remove_refref {
typedef T type;
};
template
struct emplacer< types, void>:
emplacer< types >
{
typename remove_refref::type val;
emplacer( A1 arg, Args... args ):
emplacer< types, index+1 >( std::forward(args)... ),
val( std::forward(arg) )
{}
};
template< std::size_t n >
struct extract {
template< typename A1, typename... Args >
A1&& from( emplacer&& e ) {
return extract::from( emplacer>&&(e) );
}
};
template<>
struct extract<0> {
template< typename A1, typename... Args >
A1&& from( emplacer&& e ) {
return std::move( e.val );
}
};
template
struct seq {};
template
struct make_seq: make_seq {};
template
struct make_seq<0, tail...> {
typedef seq type;
type val() { return type(); }
};
struct nothing {};
template
nothing construct( T* src, emplacer>&& e, seq s = make_seq< sizeof...(Args) >::val() ) {
new(src)T( std::move( extract( std::move(e) ))... );
}
template
emplacer< types > emplace( Args&&... a ) {
return emplacer< types >( std::forward(a)... );
}
template
struct my_array {
private:
union T_mem {
T t;
char x;
T_mem():x(0) {}
};
T_mem buff[n];
template
void do_nothing( nothings...&& ) {}
template
my_array( emplacers&&... em, seq s=make_seq< sizeof...(emplacers) >::val() ) {
do_nothing( construct( &buff[indexes].t, em)... );
}
~my_array() {
for( auto&& v:buff) {
v.t.~T();
}
}
T& operator[](std::size_t n) { return buff[n].t; }
// etc
};
The idea is that we create an array like construct that is actually an array of union
to both T
and a char
. We thus avoid actually constructing our T
, while still having proper alignment and such for one. Assuming char
has no non-trivial alignment, the resulting buffer is binary-compatible with a T[]
.
We then take as a construction argument emplacer
objects, which act as packages for arbitrary construction arguments. For annoying reasons, these objects need to create a temporary copy of some of their parameters (I cannot figure out how to avoid the lifetime issues... maybe I'm missing something).
The constructor of the my_array
takes any number of emplacers
and proceeds to construct the contents of buff
based on their arguments.
You'd create your array something like this:
my_array< Foo, 10 > arr = {
emplacer( a, b, c ),
emplacer( x, y, z ),
...
};
a bit more work would allow default construction of uninitialized objects in the array.
But this is really, really tricky to write correctly.