Value initialization c++ - c++

Reading cppreference on value initialization, I have come to this:
if T is a class type with no default constructor or with a
user-provided or deleted default constructor, the object is
default-initialized;
And example:
struct T3
{
int mem1;
std::string mem2;
T3() { } // user-provided default constructor
};
Reading article on default initialization
if T is a non-POD (until C++11) class type, the constructors are
considered and subjected to overload resolution against the empty
argument list. The constructor selected (which is one of the default
constructors) is called to provide the initial value for the new
object;
if T is an array type, every element of the array is
default-initialized;
otherwise, nothing is done: the objects with automatic storage
duration (and their subobjects) are initialized to indeterminate
values.
This applies to the example, T is class type, which means the overload resolution should select the candidate to initialize the values ( the user-provided default constructor ), but it is empty, so mem1 should stay with indetermined values (that's true ) but same should be mem2, but that is "default initialized" to "", why is that? Does it work recursively? Every member of T that is class type is subjected to first rule?
I'm quite confused right now.
2)if T is a class type with a default constructor that is neither
user-provided nor deleted (that is, it may be a class with an
implicitly-defined or defaulted default constructor), the object is
zero-initialized and then it is default-initialized if it has a
non-trivial default constructor;
And example:
struct T1
{
int mem1;
std::string mem2;
}; // implicit default constructor
the mem1 is zero-initialized to 0, however what does "non-trivial" default contructor means? mem2 is also default-initialized to "", howevever i am still unsure, what does "non-trivial default constructor" means? The default constructor should be generated by compiler, however how does it decide what is and what is not non-trivial --- if non-trivial default constructor means that it has to initialize objects -- same question as above, does it mean that every object is initialized with default constructor?

same should be mem2, but that is "default initialized" to "", why is that? Does it work recursively? Every member of T that is class type is subjected to first rule?
Your suspicion is correct. When you default initialize the class you default initialize each of its members since no initialization was specified in your constructor. Since std::string has a user provided default constructor that is called and it initializes the string object to nothing.
however what does "non-trivial" default contructor means?
A trivial constructor is a do nothing constructor. For a type T it's constructor is trivial if
The constructor is not user-provided (that is, implicitly-defined or defaulted)
T has no virtual member functions
T has no virtual base classes
T has no non-static members with brace-or-equal initializers.
Every direct base of T has a trivial default constructor
Every non-static member of class type has a trivial default constructor
So in T1's case you do not have a trivial constructor because std::string's default constructor is non trivial.

This applies to the examle, T is class type, which means the overload resolution should select the candidate to inicialize the values ( the user-provided default constructor ), but it is empty, so mem1 should stay with inderermined values (thats true ) but same should be mem2, but that is "default initialized" to "", why is that?
If you omit a data member from the member initializer list then it's default initialized, which for std::string means calling it's default constructor and initializing it to an empty string. Your default constructor has no member initializer list so all the members gets default initialized.
what does "non-trivial" default contructor means?
You can find the requirements for a trivial constructor here. A non-trivial default constructor is just a default constructor that does not respect all of the requirements for a trivial constructor. In short, a trivial default constructor doesn't do anything at all (even under the hood) other than mark the beginning of the lifetime of the object.
does it mean that every object is initialized with default constructor?
As mentioned earlier, the default constructor (like any other constructor) will default initialize any data member that isn't otherwise specified in the member initializer list. Since it's by default empty, default constructors will recursively cause default initialization unless an inner member specifies an alternative initialization using a member initializer list.

Related

How do I make user defined empty default constructor behave like compiler defined empty constructor

I've been trying to learn C++ for about a month and it still puzzles me. For example this 'easy' code:
class A
{
int m;
public:
A() = default;
int getM() { return m; }
};
class B
{
int m;
public:
// empty body and empty initializer list
B() {} // isn't this the same as "B() = default;" ?
int getM() { return m; }
};
int main()
{
A a1;
A a2{};
std::cout << "a1.m=" << a1.getM() << "\n";
std::cout << "a2.m=" << a2.getM() << "\n";
B b1;
B b2{};
std::cout << "b1.m=" << b1.getM() << "\n";
std::cout << "b2.m=" << b2.getM() << "\n";
std::cin.ignore();
}
result:
a1.m=...garbage
a2.m=0
b1.m=...garbage
b2.m=...garbage
According to CPP REFERENCE default constructor defined by the compiler does have empty body and empty initializer list. So how the heck does it (in class A) initialize member 'm' to zero when explicitly defined default constructor with empty body and empty initializer list does not. According to cppreference excerpt:
If the implicitly-declared default constructor is not defined as deleted, it is defined (that is, a function body is generated and compiled)
by the compiler if odr-used, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list.
As far as I understand it both constructors should behave exactly the same way. Seems simple yet I do not get it.
Here's the basic idea. Both A a2{}; and B b2{}; will perform what is called "value initialization" on the two objects. However, the way value initialization behaves depends on how those types are defined.
B is an object which has a user-provided default constructor. "User-provided" being the term for when you provide a body for the default constructor. Because of that, value initialization will call the default constructor. That default constructor does not initialize its members, so the members remain uninitialized.
A is an object which does not have a user-provided default constructor. Nor does it have any other user-provided constructors. And the default constructor is not deleted either. And there are no default member initializers in A. Given all of that, value initialization will perform zero initialization on the object. Which means that it will write all zeros to the memory for that object before it comes into existence.
That's what the rules say; the two do not behave the same, nor are they meant to. Nor is there anything you can do to make a user-provided default constructor act like a defaulted default constructor in all cases. You can make the user-provided constructor value initialize its members, but it would do so all the time, even if you use default initialization (B b1;, for example).
Why do the rules say that? Because = default is not supposed to be equivalent to an empty constructor body. Indeed, being different is why = default exists as a feature.
When you = default your default constructor, you are saying "generate the default constructor as you normally would". This is important, because there are things you can do which actively prevent the compiler from generating a default constructor for you. If you specify other constructors (which are not copy/move constructors), the compiler will not automatically generate one. So by using = default syntax, you're telling the compiler that you want the generated default constructor.
By contrast, if you make an empty body in your default constructor, you are saying something totally different. You are explicitly saying, "If a user calls my default constructor, I want my members to be default-initialized." That's what it means when you have an empty member initializer list in a constructor, after all. So that's what it should do.
If = default and an empty body behaved the same, there would be no way for you to get that behavior, to say that you want default initialization of your members no matter what.
Basically, Cppreference's statement is completely wrong; it does not have "exactly the same effect as a user-defined constructor with empty body and empty initializer list". Nor is it supposed to.
If you want to understand the thinking of value initialization a bit further, consider this.
int i{};
That is guaranteed to produce a value of 0 for i. It is therefore reasonable that this:
struct S{int i;};
S s{};
Should also produce a value of 0 for s.i. How does that happen? Because value initialization will zero-initialize s.
So how does a user say that they don't want that, or want some special form of initialization? You communicate that the same way you communicate everything else: you add a constructor. Specifically, a default constructor that does the form of initialization you want.
If any constructor is provided zero-initialization does not take place as stated here
Zero initialization is performed in the following situations:
...
2) As part of value-initialization sequence for non-class types and for members of value-initialized class types that have no constructors, including value initialization of elements of aggregates for which no initializers are provided.
Also here
The effects of value initialization are:
1) if T is a class type with at least one user-provided constructor of any kind, the default constructor is called;
Which makes sense. One should be able to control whether any additional operations can be added. If you provide a constructor, you take responsibility for your object initializations.

Is a constructor implicitly declared in the case of a abstract class with no data members? [duplicate]

In the book I'm reading at the moment (C++ Without Fear) it says that if you don't declare a default constructor for a class, the compiler supplies one for you, which "zeroes out each data member". I've experimented with this, and I'm not seeing any zeroing -out behaviour. I also can't find anything that mentions this on Google. Is this just an error or a quirk of a specific compiler?
If you do not define a constructor, the compiler will define a default constructor for you.
Construction
The implementation of this
default constructor is:
default construct the base class (if the base class does not have a default constructor, this is a compilation failure)
default construct each member variable in the order of declaration. (If a member does not have a default constructor, this is a compilation failure).
Note:
The POD data (int,float,pointer, etc.) do not have an explicit constructor but the default action is to do nothing (in the vane of C++ philosophy; we do not want to pay for something unless we explicitly ask for it).
Copy
If no destructor/copy Constructor/Copy Assignment operator is defined the compiler builds one of those for you (so a class always has a destructor/Copy Constructor/Assignment Operator (unless you cheat and explicitly declare one but don't define it)).
The default implementation is:
Destructor:
If user-defined destructor is defined, execute the code provided.
Call the destructor of each member in reverse order of declaration
Call the destructor of the base class.
Copy Constructor:
Call the Base class Copy Constructor.
Call the copy constructor for each member variable in the order of declaration.
Copy Assignment Operator:
Call the base class assignment operator
Call the copy assignment operator of each member variable in the order of declaration.
Return a reference to this.
Note Copy Construction/Assignment operator of POD Data is just copying the data (Hence the shallow copy problem associated with RAW pointers).
Move
If no destructor/copy Constructor/Copy Assignment/Move Constructor/Move Assignment operator is defined the compiler builds the move operators for you one of those for you.
The default implementation is:
Implicitly-declared move constructor
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
Move Constructor:
Call the Base class Copy Constructor.
Call the move constructor for each member variable in the order of declaration.
Move Assignment Operator:
Call the base class assignment operator
Call the move assignment operator of each member variable in the order of declaration.
Return a reference to this.
I think it's worth pointing out that the default constructor will only be created by the compiler if you provide no constructor whatsoever. That means if you only provide one constructor that takes an argument, the compiler will not create the default no-arg constructor for you.
The zeroing-out behavior that your book talks about is probably specific to a particular compiler. I've always assumed that it can vary and that you should explicitly initialize any data members.
Does the compiler automatically generate a default constructor?
Does the implicitly generated default constructor perform zero
initialization?
If you legalistically parse the language of the 2003 standard, then the answers are yes, and no. However, this isn't the whole story because unlike a user-defined default constructor, an implicitly defined default constructor is not always used when creating an object from scratch -- there are two other scenarios: no construction and member-wise value-initialization.
The "no construction" case is really just a technicality because it is functionally no different than calling the trivial default constructor. The other case is more interesting: member-wise value-initialization is invoked by using "()" [as if explicitly invoking a constructor that has no arguments] and it bypasses what is technically referred to as the default constructor. Instead it recursively performs value-initialization on each data member, and for primitive data types, this ultimately resolves to zero-initialization.
So in effect, the compiler provides two different implicitly defined default constructors. One of which does perform zero initialization of primitive member data and the other of which does not. Here are some examples of how you can invoke each type of constructor:
MyClass a; // default-construction or no construction
MyClass b = MyClass(); // member-wise value-initialization
and
new MyClass; // default-construction or no construction
new MyClass(); // member-wise value-initialization
Note: If a user-declared default constructor does exist, then member-wise value-initialization simply calls that and stops.
Here's a somewhat detailed breakdown of what the standard says about this...
If you don't declare a constructor, the compiler implicitly creates a default constructor [12.1-5]
The default constructor does not initialize primitive types [12.1-7]
MyClass() {} // implicitly defined constructor
If you initialize an object with "()", this does not directly invoke the default constructor. Instead, it instigates a long sequence of rules called value-initialization [8.5-7]
The net effect of value initialization is that the implicitly declared default constructor is never called. Instead, a recursive member-wise value initialization is invoked which will ultimately zero-initialize any primitive members and calls the default constructor on any members which have a user-declared constructor [8.5-5]
Value-initialization applies even to primitive types -- they will be zero-initialized. [8.5-5]
int a = int(); // equivalent to int a = 0;
All of this is really moot for most purposes. The writer of a class cannot generally assume that data members will be zeroed out during an implicit initialization sequence -- so any self-managing class should define its own constructor if it has any primitive data members that require initialization.
So when does this matter?
There may be circumstances where generic code wants to force initialization of unknown types. Value-initialization provides a way to do this. Just remember that implicit zero-initialization does not occur if the user has provided a constructor.
By default, data contained by std::vector is value-initialized. This can prevent memory debuggers from identifying logic errors associated with otherwise uninitialized memory buffers.
vector::resize( size_type sz, T c=T() ); // default c is "value-initialized"
Entire arrays of primitives type or "plain-old-data" (POD)-type structures can be zero-initialized by using value-initialization syntax.
new int[100]();
This post has more details about variations between versions of the standard, and it also notes a case where the standard is applied differently in major compilers.
C++ does generate a default constructor but only if you don't provide one of your own. The standard says nothing about zeroing out data members. By default when you first construct any object, they're undefined.
This might be confusing because most of the C++ primitive types DO have default "constructors" that init them to zero (int(), bool(), double(), long(), etc.), but the compiler doesn't call them to init POD members like it does for object members.
It's worth noting that the STL does use these constructors to default-construct the contents of containers that hold primitive types. You can take a look at this question for more details on how things in STL containers get inited.
The default constructor created for a class will not initialize built-in types, but it will call the default constructor on all user-defined members:
class Foo
{
public:
int x;
Foo() : x(1) {}
};
class Bar
{
public:
int y;
Foo f;
Foo *fp;
};
int main()
{
Bar b1;
ASSERT(b1.f.x == 1);
// We know nothing about what b1.y is set to, or what b1.fp is set to.
// The class members' initialization parallels normal stack initialization.
int y;
Foo f;
Foo *fp;
ASSERT(f.x == 1);
// We know nothing about what y is set to, or what fp is set to.
}
The compiler will generate default constructors and destructors if user-created ones are not present. These will NOT modify the state of any data members.
In C++ (and C) the contents of any allocated data is not guaranteed. In debug configurations some platforms will set this to a known value (e.g. 0xFEFEFEFE) to help identify bugs, but this should not be relied upon.
Zero-ing out only occurs for globals. So if your object is declared in the global scope, its members will be zero-ed out:
class Blah
{
public:
int x;
int y;
};
Blah global;
int main(int argc, char **argv) {
Blah local;
cout<<global.x<<endl; // will be 0
cout<<local.x<<endl; // will be random
}
C++ does not guarantee zeroing out memory. Java and C# do (in a manner of speaking).
Some compilers might, but don't depend on that.
In C++11, a default constructor generated by the compiler is marked deleted , if :
the class has a reference field
or a const field without a user-defined default constructor
or a field without a default initializer, with a deleted default constructor
http://en.cppreference.com/w/cpp/language/default_constructor
C++ generates a default constructor. If needed (determined at compile time I believe), it will also generate a default copy constructor and a default assignment constructor. I haven't heard anything about guarantees for zeroing memory though.
The compiler by default will not be generating the default constructor unless the implementation does not require one .
So , basically the constructor has to be a non-trivial constructor.
For constructor to be non-trivial constructor, following are the conditions in which any one can suffice:
1) The class has a virtual member function.
2) Class member sub-objects or base classes have non-trivial constructors.
3) A class has virtual inheritance hierarchy.

C++ object construction with garbage, why?

I have had the following structure:
struct MyStruct
{
struct Displacement
{
bool isWrapped;
int steps;
};
int initialDuration;
int currentDuration;
Displacement displacement1;
Displacement displacement2;
};
And I was using it like this:
MyStruct obj{2, 2};
auto shared = std::make_shared<MyStruct>(obj);
The problem was that I needed to duplicate first two arguments as initialDuration and currentDuration should be the same when a new object is created. Therefore I have created a CTOR:
struct MyStruct
{
struct Displacement
{
bool isWrapped;
int steps;
};
MyStruct(int duration)
: initialDuration(duration)
, currentDuration(duration)
{
}
int initialDuration;
int currentDuration;
Displacement displacement1;
Displacement displacement2;
};
and then used like this:
auto shared = std::make_shared<MyStruct>(2);
The unexpected thing 1 is: both Displacement members of MyStruct have been initialized with garbage. For one the bool member of true, for the other one - false and ints were some arbitrary numbers.
So though maybe I need to define CTOR for Displacement too. Defined like this:
struct Displacement
{
Displacement() : isWrapped(false), steps(0) {}
bool isWrapped;
int steps;
};
The unexpected thing 2 is: that somewhere else
MyStruct::Displacement d {false, 0};
started to not compile. I don't know aggregate initialization or list-initialization stopped working for a POD struct when I have defined the default CTOR.
Please explain the reasons of those 2 behaviours.
For your 1st question
both Displacement members of MyStruct have been initialized with garbage.
(from comment) There should be a default CTOR for Displacement defined by the compiler which should be called in the CTOR of MyStruct that I have defined, right?
The implicitly-defined default constructor won't do anything here.
If the implicitly-declared default constructor is not defined as deleted, it is defined (that is, a function body is generated and compiled) by the compiler, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list. That is, it calls the default constructors of the bases and of the non-static members of this class.
And for MyStruct obj{2, 2};, obj is an automatic object, so (from standard $8.5/12 Initializers [dcl.init])
If no initializer is specified for an object, the object is default-initialized. When storage for an object
with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if
no initialization is performed for the object, that object retains an indeterminate value until that value is
replaced (5.18). [ Note: Objects with static or thread storage duration are zero-initialized, see 3.6.2. —
end note ]
You might want to initialize them as
MyStruct obj{2, 2, {false, 0}, {false, 0}};
For your 2nd question
I don't know aggregate initialization or list-initialization stopped working for a POD struct when I have defined the default CTOR.
Yes it does, unless you provide another ctor which takes std::initializer_list as its parameter. See aggregate initialization
An aggregate is an object of the type that is one of the following
array type
class type (typically, struct or union), that has
no private or protected non-static data members
no user-provided constructors (explicitly defaulted or deleted constructors are allowed) (since C++11)
no base classes
no virtual member functions
no default member initializers (since C++11) (until C++14)
If you have a constructor that is called you need to explicitly initialize all members, or they will be uninitialized. Uninitialized members will have indeterminate values which is what you perceive as garbage. That's how C++ work when you have constructors.
From this implicit default constructor reference:
If the implicitly-declared default constructor is not defined as deleted, it is defined ... by the compiler, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list. That is, it calls the default constructors of the bases and of the non-static members of this class.
Built-in or aggregate types have no constructor, and so can't be constructed or initialized without an explicit initialization.
Your struct is no longer a POD type when you add a default constructor. Among other things, a POD type must have a trivial default constructor.
You need to add a constructor that takes two arguments for your code to start working again. Also note that the default constructor does not 0-initialize members. The reason it worked before with the POD type is that doing aggregate initialization (which only works with POD types) defaults members not explicitly initialized to 0.

why do I need a constructor function?

#include<iostream>
using namespace std;
class A {
public:
int i;
};
int main() {
const A aa; //This is wrong, I can't compile it! The implicitly-defined constructor does not initialize ‘int A::i’
}
when I use
class A {
public:
A() {}
int i;
};
this is ok! I can compile it! why I can't compile it when I use the implicitly-defined constructor?
why the implicit-defined constructor does not work?
It does work, but one of the language rules is that it can't be used to initialise a const object unless it initialises all the members; and it doesn't initialise members with trivial types like int. That usually makes sense, since being const there's no way to give them a value later.
(That's a slight simplification; see the comments for chapter and verse from the language standard.)
If you define your own constructor, then you're saying that you know what you're doing and don't want that member initialised. The compiler will let you use that even for a const object.
If you want to set it to zero, then you could value-initialise the object:
const A aa {}; // C++11 or later
const A aa = A(); // historic C++
If you want to set it to another value, or set it to zero without the user having to specify value-initialisation, then you'll need a constructor that initialises the member:
A() : i(whatever) {}
why the implicit-defined constructor does not work?
Because the C++ standard says so:
[dcl.init] paragraph 7:
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
This ensures that you don't create a const object containing uninitialized data that cannot be initialized later.
To initialize a const-qualified object you need to have a user-provided default constructor or use an initialiser:
const A aa = A();
Here the object aa is initialized with the expression A() which is a value-initialized object. You can value-initialize a class type without a default constructor, because value-initialization will set values to zero if there is no default constructor for the type.
However, the rule in the standard is too strict, as it forbids using implicitly-defined constructors even when there are no data members or all data members have sensible default constructors, so there is a defect report against the standard proposing to change it, see issue 253.
You don't state what compiler you're using. I've tried this with VS2012 and get a warning C4269.
The reason this is a problem is because aa is const. Because you haven't defined a constructor a default one is used and so i can be anything. It also cannot be changed (because aa is const).
If you define a constructor, it is assumed that you are happy with the initialization of i. Although, in this case you haven't actually changed the behaviour.
From this MSDN page
Since this instance of the class is generated on the stack, the initial value of m_data can be anything. Also, since it is a const instance, the value of m_data can never be changed.
Because i is not initialized.
class A
{
public:
A()
{
i =0;
}
int i;
};
"Implicit constructor" means a constructor generated for you automatically and generates an error because it realizes it is not able to initialize the value of i. This can be a no-args constructor, a copy constructor or (as of C++11) a move constructor.
why the implicit-defined constructor does not work?
It works just fine, but it does not decide what your default values are implicitly (and as such, it only calls default constructors for it's members, but not for POD types).
If you want the constructor to initialize your members with certain values you have to explicitly write that (i.e. add a default constructor explicitly).
Making a default (implicit) constructor initialize POD members with a chosen value (like zero for example) would add extra computing cycles (and slow your program down) when you don't need that. C++ is designed to behave as if you (the programmer) know what you are doing (i.e. if you do not initialize your members explicitly, the compiler assumes you don't care what default value you get).

C++11 move constructor

What would be the correct way to implement a move constructor considering the following class:
class C {
public:
C();
C(C&& c);
private:
std::string string;
}
Of course, the idea is to avoid copying string or deallocating it twice.
Lets assume the basic example is just for clarity and I do need a move constructor.
I tried:
C::C(C&& c) {
//move ctor
string = std::move(c.string);
}
And
C::C(C&& c) : string(std::move(c.string)) {
//move ctor
}
Both compile fine on gcc 4.8 and run fine. It seems option A is the correct behaviour, string gets copied instead of moved with option B.
Is this the correct implementation of a move constructor?
Since std::string itself has a move-ctor, the implicitly defined move-ctor for C will take care of the proper move operation. You may not define it yourself. However, if you have any other data member and specifically:
12.8 Copying and moving class objects
12 An implicitly-declared copy/move constructor is an inline public
member of its class. A defaulted copy- /move constructor for a class X
is defined as deleted (8.4.3) if X has:
— a variant member with a
non-trivial corresponding constructor and X is a union-like class,
— a
non-static data member of class type M (or array thereof) that cannot
be copied/moved because overload resolution (13.3), as applied to M’s
corresponding constructor, results in an ambiguity or a function that
is deleted or inaccessible from the defaulted constructor, or
— a
direct or virtual base class B that cannot be copied/moved because
overload resolution (13.3), as applied to B’s corresponding
constructor, results in an ambiguity or a function that is deleted or
inaccessible from the defaulted constructor, or
— for the move
constructor, a non-static data member or direct or virtual base class
with a type that does not have a move constructor and is not trivially
copyable.
13 A copy/move constructor for class X is trivial if it is
neither user-provided nor deleted and if
— class X has no virtual functions (10.3) and no virtual base classes (10.1), and
functions (10.3) and no virtual base classes (10.1), and
— the
constructor selected to copy/move each direct base class subobject is
trivial, and
— for each non-static data member of X that is of class
type (or array thereof), the constructor selected to copy/move that
member is trivial; otherwise the copy/move constructor is non-trivial.
you may want to implement your own move-ctor.
In case you need the move-ctor, prefer the initializer list syntax. Always! Otherwise, you may end up with a default construction per object not mentioned in the initializer list (which is what you're forced for member objects with non-default ctors only).
Both your variants will move the string. The second variant should be preferred because it will not default construct an empty string just to move assign it afterwards.
Check your testcase and then your compiler's bugzilla list. You need to trace calls to both string::operator=(string&&) (1st case) and string::string(string&&) (2nd case) if you want to ensure for both cases that they move.
Both Constructors should work. So both are correct move constructors. The second one might be more efficient, since the first one default constructs string only to assign to it, while the second will simply move construct it and should therefore be more efficient. If the second one is less efficient I would suspect a compiler bug (remember that C++11 support is still not complete for current compilers) or a flawed test methology (how exactly do you test copy vs move and are you sure the move constructor not the assignment op is called in both cases?).
Of course whenever possibly you could simply let the compiler generate your constructor via C(C&&) = default;.
There is no need to implement a move constructor here since you don't have to manually manage memory. Move constructors are only useful when you manually use dynamic arrays in your class.
You can still explicitly have the compiler create the default move constructor even though it should have already been done even if you don't request it:
C(C&& c) = default;