I have a class Thing sporting no default constructor.
Now we define another class, which now has to initalise the array elements at once, as without a default constructor, no late assignment can be made. So we have:
class TwoThings
{
public:
Thing things[2];
TwoThings() : things({Thing("thing 1"),Thing("thing 2")})
{
}
}
Is this the correct way?
GCC compiles it fine, while Clang does not, stating an "initializer list" should be used. I tried several alternative ways like double braces {{ ... }} and such, but can't manage to get a compiling equivalent for Clang.
How to initialise arrays without default constructor in Clang?
Yes, parenthesised member array initialization is a GCC extension. To make it standard, you can just use a single set of braces (requires C++11):
TwoThings() : things{Thing("thing 1"),Thing("thing 2")}
{
}
If you can't use C++11, you might want to use a different storage method, like std::pair<Thing,Thing> or std::vector<Thing>.
Related
I realized that in C++ you can initialize any structure with default values. I don't think that was possible under C. Is that correct?
Are there situations in which it still makes sense not to use such a default initialization? Or is it better in any case since it is safer?
I also saw that there are different ways to do this.
Is One Method Better Than Another? Or is it just a matter of taste?
struct STR_Foo {
int value{ 0 };
};
struct STR_Foo {
int value = 0;
};
struct STR_Foo {
int value = { 0 };
};
I realized that in C++ you can initialize any structure with default values. I don't think that was possible under C. Is that correct?
Yes.
Are there situations in which it still makes sense not to use such a default initialization?
If you target Older standard than C++11 - or cross-compatibility with C. Default member initialisers weren't in the language before that - and aren't in C.
If you target C++11 standard and you want the class to be an aggregate. Since C++14 default member initialisers don't disqualify a class from being an aggregate.
If you need an instance of the class to be initialised later and have measured that you cannot afford the very low overhead of redundant initialisation.
I also saw that there are different ways to do this. Is One Method Better Than Another? Or is it just a matter of taste?
The choice to use = or not is stylistic when using curly brackets. Attempting to initialise without = and parentheses instead of curlies ends up being a function declaration for some cases, so you need to disambiguate by using = or curlies.
Curly brackets affect the form of initialisation in some cases. In cases where curlies and no curlies invoke the same constructor, using curlies is recommended because that syntax don't allow narrowing conversions. When curlies invoke a different constructor than no curlies, use the form that does what you need.
These apply to all initialisation; not just default members.
I am writing a C++ application that makes use of plenty of child objects nested within one 'root' object.
Each of these needs to be initialized in a specific order, with various operations being done between each initialization, and sometimes needing to have previously-initialized ones as arguments to each other.
However, my compiler is forcing me to initialize these in the constructor's initializer list, insisting that initializing within the constructor is not enough. For example, I could not do the following:
SomeThing::SomeThing() {
int argGottenByLibraryCall;
someLibraryCallToGetArg(&argGottenByLibraryCall);
m_ChildObject = ChildClass(argGottenByLibraryCall);
}
I have taken to implementing a post-creation initializer function instead:
SomeThing::SomeThing() : m_ChildObject() {
int argGottenByLibraryCall;
someLibraryCallToGetArg(&argGottenByLibraryCall);
m_ChildObject.Initialize(argGottenByLibraryCall);
}
This, however, seems like a poor practice for me to follow. Is there a better way of making my compiler (VC++ 2017) accept initialization within the constructor body?
This, however, seems like a poor practice for me to follow.
The poor practice is not using initialization lists. They allow you to mark your data members as const and also ensure that they are initialized.
The correct way to solve your problem is to restructure your code so that m_ChildObject has a proper constructor that can be used in the member initialization list.
If that is not possible, wrap your initialization logic in a function and use that in the member initialization list:
ChildObjectType makeChildObject() {
int argGottenByLibraryCall;
someLibraryCallToGetArg(&argGottenByLibraryCall);
ChildObjectType result;
result.Initialize(argGottenByLibraryCall);
return result;
}
// ...
SomeThing::SomeThing() : m_ChildObject(makeChildObject()) { }
struct SalesData {
SalesData() = default;
SalesData(const string&s) : bookNo(s) {}
string bookNo;
unsigned copies_sold = 0;
double revenue = 0;
};
While discussing default constructors, primer says we can use the keyword "=default" to make a constructor default just like code above. That keyword can appear with the declaration inside the class, or with the definition outside the class.
If the compiler can't use in-class initializers, we can use initializer list just like the second constructor.
But what's the format?
First I tried to use initializer list directly:
SalesData() = default:bookNo(""), copies_sold(1), revenue(2){};
But it can't work, the compiler says there shouldn't be a colon afer "default".
Then I tried to declare the constructor with "=default" in class and define it outside, without using the initializer list:
struct SalesData {
SalesData() = default;
/*...*/}
SalesData::SalesData() {
bookNo = "";
copies_sold + 0;
revenue = 0;
}
The compiler says it's an error of redefinition.
Then I moved the keyword outside, which the primer says is supposed to be ok:
struct SalesData {
SalesData();
/*...*/}
SalesData::SalesData()=default {
bookNo = "";
copies_sold + 0;
revenue = 0;
}
It still failed, the compiler said there should be a semicolon behine the "default".
I've googled for similar questions, but nothing found.
How could the "=default" and initializer list work together?
How should I use the "=default" outside the class body?
If a function is declared =default, you cannot provide an initializer list (for a constructor), nor a function body.
Either the book is wrong or you misunderstood what the book is saying.
In certain situations the compiler automatically generates special functions like the default ctor for you. =default is a way to tell the compiler explicitly to generate the function even in a situation where that would not happen automatically.
The point is: You either implement the function yourself as usual or you =default it. You cannot do both for the same function. The only way to influence what the compiler generates is via default initializers like your double revenue = 0;
Compiler generated implementations are available for the default ctor, copy ctor, copy assignment, move ctor, move assignment and destructor. See this answer for more details. SalesData(const string&) is neither of those, so it cannot be =defaulted.
Some additions.
If you do not specify any constructor in your class the compiler generates the default constructor. This constructor just initializes all the members with their default (empty) constructor (naturally if that constructor exists otherwise it runs to compilation error).
If you specify a non-default constructor the compiler does not generate the default one automatically. Here you can find some details:
Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?
The =default keyword means that you explicitly ask the compiler to generate the default constructor with default body (also if you already specified another one). This means the body of this constructor is created by the compiler not by you. This is the reason why you got compilation error if you tried to define it.
Note that the =default keyword can be used also at destructors.
Rather surprised to find this question not asked before. Actually, it has been asked before but the questions are VERY DIFFERENT to mine. They are too complicated and absurd while I'll keep it simple and to the point. That is why this question warrants to be posted.
Now, when I do this,
struct A {
int a = -1;
};
I get the following error:
ANSI C++ forbids in-class initialization of non-const static member a
Now, along with the workaround can someone please tell me THE BEST way of initializing a struct member variable with a default value?
First, let's look at the error:
ANSI C++ forbids in-class initialization of non-const static member a
Initialization of a true instance member, which resides within the memory of an instance of your struct is the responsibility of this struct's constructor.
A static member, though defined inside the definition of a particular class/struct type, does not actually reside as a member of any instances of this particular type. Hence, it's not subject to explaining which value to assign it in a constructor body. It makes sense, we don't need any instances of this type for the static member to be well-initialized.
Normally, people write member initialization in the constructor like this:
struct SomeType
{
int i;
SomeType()
{
i = 1;
}
}
But this is actually not initialization, but assignment. By the time you enter the body of the constructor, what you've done is default-initialize members. In the case of a fundamental type like an int, "default-initialization" basically boils down to "eh, just use whatever value was in those bytes I gave you."
What happens next is that you ask i to now adopt the value 1 via the assignment operator. For a trivial class like this, the difference is imperceptible. But when you have const members (which obviously cannot be tramped over with a new value by the time they are built), and more complex members which cannot be default-initialized (because they don't make available a visible constructor with zero parameters), you'll soon discover you cannot get the code to compile.
The correct way is:
struct SomeType
{
int i;
SomeType() : i(1)
{
}
}
This way you get members to be initialized rather than assigned to. You can initialize more than one by comma-separating them. One word of caution, they're initialized in the order of declaration inside your struct, not how you order them in this expression.
Sometimes you may see members initialized with braces (something like i{1} rather i(c)). The differences can be subtle, most of the time it's the same, and current revisions of the Standard are trying to smooth out some wrinkles. But that is all outside the scope of this question.
Update:
Bear in mind that what you're attempting to write is now valid C++ code, and has been since ratification of C++11. The feature is called "Non-static data member initializers", and I suspect you're using some version of Visual Studio, which still lists support as "Partial" for this particular feature. Think of it as a short-hand form of the member initialization syntax I described before, automatically inserted in any constructor you declare for this particular type.
You could make a default constructor
struct A {
A() : a{-1} {}
int a;
};
C++11 introduced this:
struct MyClass {
int foo = 0; //*
};
Until now I've been using this without thinking about it, but now I'm wondering:
Is this initialization doing/executing any actual initialization at this particular line (//* in the code), or is this a mere convenience notation that only does/executes something later, when the object is actually constructed?
Not sure what you mean by "later" and "at this particular line", but the above is equivalent to the following:
struct MyClass {
MyClass() : foo(0) { }
};
So if I understand your question correctly, then the answer is: "Yes, only when the object is actually constructed".
Declarations are not executable code, they do not execute anything. This is merely a convenient notation for inserting initialization of foo to zero into every constructor that you define (or into an implicitly defined default constructor, if you do not define any constructors yourself).