Here's a minimal example exposing the problem:
struct noncopyable
{
noncopyable(int) {};
noncopyable(noncopyable const&) = delete;
};
int main()
{
noncopyable f0 = {1};
noncopyable f1 = 1;
}
Although the two initializations of f0
and f1
have the same form (are both copy-initialization), f0
uses list-initialization which directly calls a constructor, whereas the initialization of f1
is essentially equivalent to foo f1 = foo(1);
(create a temporary and copy it to f1
).
This slight difference also manifests in the array case:
noncopyable f0[] = {{1}, {2}, {3}, {4}};
noncopyable f1[] = {1, 2, 3, 4};
Aggregate-initialization is defined as copy-initialization of the members [dcl.init.aggr]/2
Each member is copy-initialized from the corresponding initializer-clause.
Therefore, f1
essentially says f1[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 constructors explicit
;) 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
struct expl
{
explicit expl(int) {};
};
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.