c++ default argument vs. initalizer list - c++

There are four specific cases , where you must specify default initializer list.
Fully discussed here
In short, if you have
Initializer list Required
non-static const data members
data member of reference type
provide params to data members which are objects of other class
provide base class ctor parameters from derived class.
The one drawback to default argument seems to be that the default arguments must be the trailing parameters in the function prototype parameter list. For example:
drawbacks(???) to default arguments
void f(int, int = 2, int = 3); // trailing defaults
void g(int = 1, int = 2, int c); // error
void h(int, int = 3, int); // error
My question is, if my code does not fall in between the 4 required cases for initializer list and always requires all parameters to have default values, i.e. does not lead to the drawbacks to default arguments , which one should I choose and why? What is the best practice?
an example being
// option -1: for the default argument list
// my_array.h
my_array(int a_number_of_elements = 0, int default_val = 0);
//option-2 : default initalizer list
// my_array.h
my_array(int a_number_of_elements, int default_val);
//my_array.cpp
my_array:: my_array(int a_number_of_elements, int default_val)
: my_num_elements(a_num_elements), my_default_value(default_val)
Thanks for looking.

You are dealing with two completely different things here: constructor initializer lists vs default function arguments.
You got most of what you said correctly, but contrary to what you might believe, default constructor arguments do not imply the construction of data members using these values, unless you explicitly state so. Hence, one technique is not a replacement for the other, just separate complementary things.
For example:
// my_array.h
my_array(int a_number_of_elements = 1, int default_val = 0);
//my_array.cpp
my_array:: my_array(int a_number_of_elements, int default_val)
: my_num_elements(a_num_elements), my_default_value(default_val)
will [be expected to] initialize an array with one element of value zero.
If you do not explicitly call the constructors of the data members, then their default constructors, if available, will be called, so the following two are equivalent.
//implicitly default constructing members
my_array:: my_array()
{}
//explicitly default constructing members
my_array:: my_array() :
my_num_elements(), my_default_value()
{}
Regarding the two above, it is always a good idea to leave things explicit for better legibility. As often said, a given code is usually written once, but read many times.

You have a couple misconceptions here:
You are referring to "Constructor Initializer Lists" as initializer_lists
You seem to think that defaulted arguments can only be used separately from "Constructor Initialization Lists"
Lets talk about some principles here:
You'll always want to use a "Constructor Initializer List" for all your variables, not just those of the 4 types you mention. "Constructor Initializer Lists" value initialize, rather than default initializing and requiring you to assign to them in the constructor body
Where possible you'll always want to have a default constructor, as this is required for using your object in standard containers
Defaulting arguments is a great way to prevent code duplication for both a user defined constructor and a user defined default constructor
So using these principles I can say that the answer to your question is yes, that the ideal situation would be to to use both "Constructor Initializer Lists" and default arguments. Resulting in a constructor that looks something like:
my_array(int a_number_of_elements = 0, int default_val = 0) :
my_num_elements(a_number_of_elements),
my_default_value(default_val) {}

Related

Pass function parameter directly to class variable

I have a class lazy_segment_tree. This is the current constructor
template<typename T>
struct lazy_segment_tree{
int n;
int H;
T base;
vector<T> segtree;
vector<T> lazytree;
T (*join)(T,T);
T (*assign)(int,T,T);
lazy_segment_tree(vector<T> &seq, T (*merge)(T,T), T (*create)(int, T,T), T defvalue){
join=merge;
assign=create;
base=defvalue;
n=seq.size();
}
};
Can't I directly make the construction parameters go to the values in the class variable?
I am not 100% sure what do you mean by 'directly'.
But in this case you should use initializer list to initialize the member variables. More on initializer lists here.
Applied to your code, the constructor would now look like this:
lazy_segment_tree(vector<T> &seq, T (*merge)(T,T), T (*create)(int, T,T), T defvalue)
: join(merge)
, assign(create)
, base(defvalue)
, n(seq.size())
{}
In your original code, all the members are first default-constructed during initialization of the class. Then, the body of the constructor is called where you use '=' to copy assign the constructor parameters.
When initializer list is used, the members are directly constructed with specified parameters.
Depending on what T might be, it may or may not make real difference. Nevertheless, initializer lists are the standard way to initialize class members and you should use it if possible.

Base/Default Constructor vs Constructor Initialization list

I read a few different topics on these forums and I'm not getting a clear answer on it.
I have two questions, what is the difference between a default/base construct and a construct initialize list? They look the same to me, here are my examples. Do I have them commented correctly as well as what they are?
//In my .h file:
class Complex {
public:
Complex();
Complex(double, double);
private:
double real;
double imag;
};
//In my implementation file:
Complex::Complex() :real(NULL), imag(NULL) // initialization list
{
}
Complex::Complex(double inReal, double inImag) // default constructor
{
real = inReal;
imag = inImag;
}
I have two questions, what is the difference between a default/base construct and a construct initialize list?
A default constructor is a constructor that will be invoked when you create an object but don't specify any constructor arguments. For example, all these variables will be initialised by a call to the default constructor:
Complex myComplex1;
Complex myComplex2();
Complex myComplex3{};
Some other constructors do require arguments, so they're not default constructors.
In the implementation of any constructor (whether a default constructor or not) for an object with bases or member variables or constants, you can use an initialisation list to construct/initialise those bases or members.
They look the same to me, here are my examples. Do I have them commented correctly as well as what they are?
You didn't have them commented correctly. A fixed version is:
Complex::Complex() // default constructor as it does not require args
: real{}, imag{} // initialization list: sets both to 0.0
{ }
Complex::Complex(double inReal, double inImag) // NOT a default constructor
// as it requires arguments
: real(inReal), imag(inImag) // initialisation list
{
// real = inReal; // set in the initialisation list instead
// imag = inImag;
}
The non-default constructor above is invoked when you create an object while specifying matching constructor arguments:
Complex myComplex4(3.1, 2);
Complex myComplex5{-2.3, 2.7};
It's a good idea to preferentially use an initialisation list to construct your bases and set your members: otherwise at best they'll be default initialised then assigned to, which can be less efficient, but for references and constants you simply have to use an initialisation list.
It is possible to create a default constructor with arguments with default values:
Complex(double inReal = 0.0, double inImag = 0.0)
: real(inReal), imag(inImag)
{ }
This single constructor can reasonably replace both the constructors in your code.
In this case,
Complex::Complex() : real(NULL), imag(NULL)
{
}
would be your default constructor, because it is the one here that has no parameters.
An initializer list is what you see following your default constructor's name, being
:real(NULL), imag(NULL)
This is a list of member initializations that take place upon calling the default constructor Complex::Complex()
As an aside, NULL is a value meant to represent a pointer that does not point to any valid addressable memory location. It's not exactly applicable to double types.

What's the differences between member initializer list and default member initializer on non-static data member?

I'd like to understand what's the differences of using one form rather than the other (if any).
Code 1 (init directly on variables):
#include <iostream>
using namespace std;
class Test
{
public:
Test() {
cout<< count;
}
~Test();
private:
int count=10;
};
int main()
{
Test* test = new Test();
}
Code 2 (init with initialization list on constructor):
#include <iostream>
using namespace std;
class Test
{
public:
Test() : count(10) {
cout<< count;
}
~Test();
private:
int count;
};
int main()
{
Test* test = new Test();
}
Is there any difference in the semantics, or it is just syntactic?
Member initialization
In both cases we are talking about member initialization.
Keep in mind that the members are initialized in the sequence in which they are declared in the class.
Code 2: Member initializer list
In the second version:
Test() : count(10) {
: count(10) is a constructor initializer (ctor-initializer) and count(10) is a member initializer as part of the member initializer list. I like to think of this as the 'real' or primary way that the initialization happens, but it does not determine the sequence of initialization.
Code 1: Default member initializer
In the first version:
private:
int count=10;
count has a default member intitializer. It is the fallback option. It will be used as a member initializer if none is present in the constructor, but in the class the sequence of members for initialization is determined.
From section 12.6.2 Initializing bases and members, item 10 of the standard:
If a given non-static data member has both a
brace-or-equal-initializer and a mem-initializer, the initialization
specified by the mem-initializer is performed, and the non-static data
member’s brace-or-equal-initializer is ignored. [ Example: Given
struct A {
int i = / some integer expression with side effects / ;
A(int arg) : i(arg) { }
// ...
};
the A(int) constructor will simply initialize i to the value of arg,
and the side effects in i’s brace-or-equalinitializer will not take
place. —end example ]
Something else to keep in mind would be that if you introduce a non-static data member initializer then a struct will no longer be considered an aggregate in C++11, but this has been updated for C++14.
Differences
what's the differences of using one form rather than the other (if
any).
The difference is the priority given to the two options. A constructor initializer, directly specified, has precedence. In both cases we end up with a member initializer via different paths.
It is best to use the default member initializer because
then the compiler can use that information to generate the constructor's initializer list for you and it might be able to optimize.
You can see all the defaults in one place and in sequence.
It reduces duplication. You could then only put the exceptions in the manually specified member initializer list.
In the C++ Core Guidelines (see note 1 below), Guideline C.48 recommends the first approach (in-class initializers.) The reasoning provided is:
Makes it explicit that the same value is expected to be used in all constructors. Avoids repetition. Avoids maintenance problems. It leads to the shortest and most efficient code.
In fact if your constructor does nothing but initialize member variables, as in your question, then Guideline C.45 is firmer still, saying to use in-class initializers for sure. It explains that
Using in-class member initializers lets the compiler generate the function for you. The compiler-generated function can be more efficient.
I am not going to argue with Stroustrup, Sutter, and several hundred of their friends and colleagues even if I haven't written a compiler so I can't prove it's more efficient. Use in-class initializers wherever you can.
If you're not familiar with the guidelines do follow the links to see sample code and more explanations.
The difference I can think of is that member initializer list is prior to default member initializer.
Through a default member initializer, which is simply a brace or
equals initializer included in the member declaration, which is used
if the member is omitted in the member initializer list.
If a member has a default member initializer and also appears in the
member initialization list in a constructor, the default member
initializer is ignored.
For example:
class Test
{
public:
Test() {} // count will be 10 since it's omitted in the member initializer list
Test(int c) : count(c) {} // count's value will be c, the default member initializer is ignored.
private:
int count = 10;
};
There is no difference in the code. The difference would come if you would be would have more than one constructor overload and in more than one count would be 10. With the first version you would have less writing to do.
class Test
{
public:
Test() = default;
Test(int b) : b(b) {} // a = 1, c = 3
~Test();
private:
int a = 1;
int b = 2;
int c = 3;
};
As opposed to the second version where the above code would look like this:
class Test
{
public:
Test() : a(1), b(2), c(3) {}
Test(int b) : a(1), b(b), c(3) {}
~Test();
private:
int a;
int b;
int c;
};
The difference gets bigger with more member variables.
When you initialise next to the declaration of the member, this is valid only in C++11 onwards, so if you're in C++98/03 you outright cannot do this.
If the value never changes, you could choose to make this a constexpr static instead and the compiler would then be required to not use any extra storage for the value (so long as you don't define it) and instant use constant propagation wherever the value is used instead.
One disadvantage of using the by-declaration syntax is that it must be in the header, which will result in a recompile of all translation units that include the header every time you want to change its value. If this takes a long time, that might be unacceptable.
Another difference is that using the member initialisation list lets you change the value for each constructor, whilst using the by-declaration version only allows you to specify one value for all constructors (although you could overwrite this value ... but I'd personally avoid this as it could get quite confusing!).
As an aside, there's no need to use new here to create an instance of Test. This is a common mistake when people come to the language from other languages and I wanted to make you aware. There are of course many uses for doing this outside of your example.

default constructor that has the option to take in a parameter?

Is it possible to have 1 constructor have the option of being a default constructor if a parameter is not passed in.
Example, instead of having 2 constructors, where 1 is the default constructor and another is a constructor that initializes numbers passed in, is it possible to only have 1 constructor that if a value is passed in, set that value to a member function, and if no value is passed in, set the member function to a number.
example:
WEIGHT.H file:
class Weight
{
public:
Weight() { size = 0; }
Weight(int a) : size(a) {}
int size;
};
MAIN.CPP file:
int main(void)
{
Weight w1;
Weight w2(100);
}
I've been working on different school projects and they all require to have different types of constructors, and i'm wondering if there is a way to only have it once so it saves time.
Thanks for the help.
Yes, a constructor parameter may have a default argument, just like other functions can. If all of the parameters of a constructor have default arguments, the constructor is also a default constructor. So, for example,
class Weight
{
public:
explicit Weight(int a = 0) : size(a) { }
int size;
};
This constructor may be called with a single argument or with no arguments; if it is called with no arguments, 0 is used as the argument for the a parameter.
Note that I've also declared this constructor explicit. If you have a constructor that may be called with a single argument, you should always declare it explicit to prevent unwanted implicit conversions from occurring unless you really want the constructor to be a converting constructor.
(If you aren't familiar yet with converting constructors or implicit conversions, that's okay; just following this rule is sufficient for most of the code you'll ever write.)
Yes its possible as suggested by James but as you know if you are not defining the Default constructor the compiler would take over the definition part if you have not provided any constructor definition.
Its not an issue as such but its a better practice to define the Default constructor for proper initialization of values.
Google C++ Style guide also recommends it.

C++: Where to initialize variables in constructor [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
C++ initialization lists
What are the pros/cons of initializing variables at option 1 vs option 2?
class MyClass
{
public:
MyClass( float f, char a );
private:
float mFloat;
char mCharacter;
bool mBoolean;
int mInteger;
};
MyClass::MyClass( float f, char a ) : mFloat( f ), mBoolean( true ) // option 1.
{
// option 2
mCharacter = a;
mInteger = 0;
}
Edit:
Why is option 2 so common?
In short, always prefer initialization lists when possible. 2 reasons:
If you do not mention a variable in a class's initialization list, the constructor will default initialize it before entering the body of the constructor you've written. This means that option 2 will lead to each variable being written to twice, once for the default initialization and once for the assignment in the constructor body.
Also, as mentioned by mwigdahl and avada in other answers, const members and reference members can only be initialized in an initialization list.
Also note that variables are always initialized on the order they are declared in the class declaration, not in the order they are listed in an initialization list (with proper warnings enabled a compiler will warn you if a list is written out of order). Similarly, destructors will call member destructors in the opposite order, last to first in the class declaration, after the code in your class's destructor has executed.
Although it doesn't apply to this specific example, Option 1 allows you to initialize member variables of reference type (or const type, as pointed out below). Option 2 doesn't. In general, Option 1 is the more powerful approach.
See Should my constructors use "initialization lists" or "assignment"?
Briefly: in your specific case, it does not change anything. But:
for class/struct members with constructors, it may be more efficient to use option 1.
only option 1 allows you to initialize reference members.
only option 1 allows you to initialize const members
only option 1 allows you to initialize base classes using their constructor
only option 2 allows you to initialize array or structs that do not have a constructor.
My guess for why option 2 is more common is that option 1 is not well-known, neither are its advantages. Option 2's syntax feels more natural to the new C++ programmer.
Option 1 allows you to use a place specified exactly for explicitly initializing member variables.
Option 1 allows you to initialize const members. This cannot be done with option 2 (as they are assigned to, not initialized).
Why must const members be intialized in the constructor initializer rather than in its body?
There are many other reasons. You should always initialize all member variables in the initialization list if possible.
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6