I'm trying to create and initialize a class that contains a member array of a non-trivial class, which contains some state and (around some corners) std::atomic_flag
. As of C++11 one should be able to initialize member arrays.
The code (stripped down to minimum) looks like this:
class spinlock
{
std::atomic_flag flag;
bool try_lock() { return !flag.test_and_set(std::memory_order_acquire); }
public:
spinlock() : flag(ATOMIC_FLAG_INIT){};
void lock() { while(!try_lock()) ; }
void unlock() { flag.clear(std::memory_order_release); }
};
class foo
{
spinlock lock;
unsigned int state;
public:
foo(unsigned int in) : state(in) {}
};
class bar
{
foo x[4] = {1,2,3,4}; // want each foo to have different state
public:
//...
};
If I understand the compiler output correctly, this seems not to construct the member array, but to construct temporaries and invoke the move/copy constructor, which subsequently calls move constructors in sub-classes, and that one happens to be deleted in std::atomic_flag
. The compiler output that I get (gcc 4.8.1) is:
[...] error: use of deleted function 'foo::foo(foo&&)'
note: 'foo::foo(foo&&)' is implicitly deleted because the default definition would be ill-formed
error: use of deleted function 'spinlock::spinlock(spinlock&&)'
note: 'spinlock::spinlock(spinlock&&)' is implicitly deleted because [...]
error: use of deleted function 'std::atomic_flag::atomic_flag(const std::atomic_flag&)'
In file included from [...]/i686-w64-mingw32/4.8.1/include/c++/atomic:41:0
[etc]
If I remove the array and instead just put a single foo
member inside bar
, I can properly initialize it using standard constructor initializers, or using the new in-declaration initialization, no problem whatsoever. Doing the same thing with a member array fails with the above error, no matter what I try.
I don't really mind that array elements are apparently constructed as temporaries and then moved rather than directly constructed, but the fact that it doesn't compile is obviously somewhat of a showstopper.
Is there a way I either force the compiler to construct (not move) the array elements, or a way I can work around this?
Here's a minimal example exposing the problem:
Although the two initializations of
f0
andf1
have the same form (are both copy-initialization),f0
uses list-initialization which directly calls a constructor, whereas the initialization off1
is essentially equivalent tofoo f1 = foo(1);
(create a temporary and copy it tof1
).This slight difference also manifests in the array case:
Aggregate-initialization is defined as copy-initialization of the members [dcl.init.aggr]/2
Therefore,
f1
essentially saysf1[0] = 1, f1[1] = 2, ..
(this notation shall describe the initializations of the array elements), which has the same problem as above. OTOH,f0[0] = {1}
(as an initialization) uses list-initialization again, which directly calls the constructor and does not (semantically) create a temporary.You could make your converting constructorsexplicit
;) this could avoid some confusion.Edit: Won't work, copy-initialization from a braced-init-list may not use an explicit constructor. That is, for
the initialization
expl e = {1};
is ill-formed. For the same reason,expl e[] = {{1}};
is ill-formed.expl e {1};
is well-formed, still.