Reputation: 75
Without defining a constructor I am able to use memberwise initialization:
struct B {
A a_;
};
and later on it okay to say
B b {A()};
This is efficient as no copy or move of A is involved. However as soon as I define a move constructor for B the above code no longer compiles. How can I have both memberwise initialization and some constructors coexist?
Upvotes: 2
Views: 1444
Reputation: 2234
The answer given by R Sahu is already correct, but I'd like to add that there is no loss of efficiency to add a constructor that does what your memberwise-initialization does:
struct B {
A a_;
B(const A& a) : a_(a) {}
}
Now calling B b {A()};
is exactly as efficient as it was when you were using aggregate-initialization.
UPDATE: After reading the comments I've tested my assertion:
struct Foo
{
Foo() { std::cout << "Foo default" << std::endl; }
Foo(const Foo& other) { std::cout << "Foo copy" << std::endl; }
};
struct Bar
{
Foo f;
// #1 Bar(const Foo& f) : f(f) {}
};
int main()
{
Bar b { Foo() };
}
As shown above this application outputs just
Foo default
If I uncomment #1 I then get
Foo default Foo copy
as the original poster suggested (GCC 4.9 with -O3). I have to play more with non-printing constructors to see if this is always true or just when there are side effects, but I wanted to update this post to clarify that it is not as cut and dry as I suggested.
Upvotes: 1
Reputation: 206717
The way it is defined, B
is an aggregate type if A
is an aggregate type.
B b {A()};
is aggregate initialization.
When you add a move constructor to B
, B
stops being an aggregate type. Hence, you can't use aggregate initialization to initialize an instance of B
.
From the standard (emphasis mine):
8.5.1 Aggregates [dcl.init.aggr]
1 An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equalinitializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3)
Upvotes: 3