Pass function parameter directly to class variable - c++

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.

Related

Can enums be initialized in an member initialization list?

I know I can initialize int variables in a constructor with a member initialization list, but can I initialize an enum type in an member initialization list like in the example below?
enum number{zero, one, two, three};
class Example{
int test;
number number_enum;
public:
Example(int test_arg, number number_enum_arg): test(test_arg),
number_enum(number_enum_arg){
}
};
Also, I know it is better to initialize variables using an member initialization list, rather than using the assignment operator, but exactly is that?
The code in your example will work, you can initialize enums and virtually all types in an initializer list. Add a line to your constructor to print it out, you'll see.

Can't initialize field outside initializer list

I'm having trouble with something that seems very easy, so I must be overlooking something.
I need to construct a class that has a field that is also a class (non-POD). The class of the field has a default constructor and a "real" constructor. The thing is that I really can't construct the field in the initializer list, because in reality the constructor has a parameter that is a vector which needs a somewhat complex for loop to fill.
Here is a minimal example that reproduces the problem.
ConstructorsTest.h:
class SomeProperty {
public:
SomeProperty(int param1); //Ordinary constructor.
SomeProperty(); //Default constructor.
int param1;
};
class ConstructorsTest {
ConstructorsTest();
SomeProperty the_property;
};
ConstructorsTest.cpp:
#include "ConstructorsTest.h"
ConstructorsTest::ConstructorsTest() {
the_property(4);
}
SomeProperty::SomeProperty(int param1) : param1(param1) {}
SomeProperty::SomeProperty() : param1(0) {} //Default constructor, doesn't matter.
But this gives a compile error:
ConstructorsTest.cpp: In constructor 'ConstructorsTest::ConstructorsTest()':
ConstructorsTest.cpp:4:19: error: no match for call to '(SomeProperty) (int)'
the_property(4);
^
It gives no suggestions like it usually would of what functions could have been intended instead.
In the above example I would just initialize the_property in the initializer list, but in reality the 4 is actually a complex vector that needs to be generated first, so I really can't. Moving the_property(4) to the initializer list causes the compilation to succeed.
Other similar threads mention that the object must have a default constructor, or that it can't be const. Both requirements seem to have been met, here.
You can't initialize data member inside the constructor's body. (the_property(4); is just trying to invoke the_property as a functor.) You can only assign them like:
ConstructorsTest::ConstructorsTest() {
the_property = ...;
}
but in reality the 4 is actually a complex vector that needs to be generated first
You can add a member function which generate the necessary data, and use it to initialize the data member in member initializer list. e.g.
class ConstructorsTest {
...
static int generateData();
};
int ConstructorsTest::generateData() {
return ...;
}
ConstructorsTest::ConstructorsTest() : the_property(generateData()) {
}
You cannot initialize a variable twice.1 When your constructor has started, all member subobjects will have been constructed. If you do not provide a member initializer in the constructor, or a default member initializer in the class definition, then it will perform default initialization. Regardless of what form it takes, you can't construct it again.
Complex multi-statement initialization is best done via a lambda function:
ConstructorsTest::ConstructorsTest()
: the_property( []{ /* Do Complex Initialization */}() )
{
}
1: Well... you can, but not like that. And you really shouldn't for cases as simple as this.

Can I call optional::emplace as a member initializer?

I'm trying to add a new constructor an existing class and wondering if I can somehow do an emplace initialization of an optional and then use that value in the initializer for another member value.
For example:
class sample {
my_type sc;
optional<opt_type> run;
sample() :
sc( gen_default() ) {
}
enum ctor_alt { ctor_alt };
sample( ctor_alt ) :
emplace( run, ctor_arg ), /* Possible somehow? */
sc( run.field ) {
}
My primary motivation is that I don't want to alter the type of my_type. There are many users of this variable and putting it in a wrapper would require changing a lot of code.
emplace is a member function, and you cannot execute a member function until you've constructed the object (unless you're the object's constructor). But if you want to construct an engaged optional<T> in-place, you can simply use the std::in_place constructor:
run( std::in_place, ctor_arg )
However, member initializers are always executed in the order in which the members are declared, not the order in the member initializer list (your compiler ought to warn you if you initialize them out of order). So if you want to use an earlier initializer to initialize a later variable, you have to order them correctly in your declarations:
optional<opt_type> run;
my_type sc;
Sure thing, just call the constructor of std::optional with std::in_place_t:
run{std::in_place, ctor_arg}
std::in_place_t is a tag type to disambiguate when you want the type to construct the value in place instead of copying it.
If you want to use the value of the optional for another member variable, you need to make sure that that member variable comes after the optional, because member objects are initialized in the order that they appear.

Clarification of some details on the Class definition and implementation in C++ for a beginner

I am a beginner in C++ and am trying to learn it by looking at examples.
Here is an example definition for a class that I don't understand the meaning completely:
class MyClass{
public:
std::string name;
void *data;
MyClass(const std::string& t_name, void *t_data) : name(t_name), data(t_data) {}
};
Here is what I understand:
name and *data are variables of the class and, MyClass(...) is the constructor. The meaning of : is the left side class is derived from the right hand side class. However, what is the meaning of this part of the code:
MyClass(const std::string& t_name, void *t_data) : name(t_name), data(t_data) {}
Here are the questions:
what are "t_data" and "t_name"? Are they initial values for "data" and "name"? what is the reason t_ is used here?
what is the meaning of : in the above line?
what is {} at the end of that line?
Thanks for the help.
TJ
what are "t_data" and "t_name"? Are they initial values for "data" and "name"?
They are the arguments passed to the constructor. If an object were created as
MyClass thing("Fred", some_pointer);
then, in the constructor, t_name has the value "Fred" and t_data has the value of some_pointer.
what is the reason t_ is used here?
Some people like to tag the arguments to give them different names to the class members, but there's no need to do that unless you want to.
what is the meaning of : in the above line?
That marks the start of the initialiser list, which initialises the class member variables. The following initialisers, name(t_name), data(t_data) initialise those members with the constructor's arguments.
what is {} at the end of that line?
That's the constructor's body, like a function body. Any code in their will be run after the members have been initialised. In this case, there's nothing else to do, so the body is empty.
It is good C++ practice to use initializer lists to initialize members.
t_name, t_data are the parameter names.
The "t_" prefix is merely used to distinguish it from the similarly named member fields.
The members are being initialized using syntax that resembles a function call, but it is a proper initialization/construction, so you should think of it that way.
The colon signals the beginning of an initializer list
The empty braces {} designate that the body of the constructor (which happens after the members are initialized) is empty.
MyClass(const std::string& t_name, void *t_data) : name(t_name), data(t_data) {}
is constructor of your class, and you should initialize your class with a string and void* parameter, then you initialize your class fields (name and data) with your passed parameters
This is a somewhat compressed example for a beginner, but you're asking good questions.
1) t_data and t_name are the parameters going into the constructor. When you create an instance of this class, you'll do something like:
MyClass * myNewClassInstance = new MyClass("My name", &myData);
Now the constructor has "My name" (in std::string form) as t_name and a pointer to myData as t_data.
2) The colon here does not refer to a derived class. Here it says that an "initializer list" is to follow. Check out Prashant's answer's link, or do a little digging about this. Basically, this is setting the member variable name to t_name (which is the std::string that we passed into the constructor in the above example), and data to t_data (the void pointer). (see additional notes below)
3) The {} is the actual definition of the constructor. It's empty -- there's no code here. So all the constructor will do is initialize the member variables (as defined by the initializer list), and nothing more.
Now let's look at the exact same class in a more beginner-friendly format:
// The declaration of MyClass. This would often show up in a header (.h) file
class MyClass {
public:
// Member variables
std::string name;
void * data;
// Declaration of constructor -- we haven't defined what actually does here, just
// what parameters it takes in.
MyClass(const std::string& t_name, void * t_data);
}
// We declared what the class "looks like" above, but we still have to define what
// the constructor does. This would usually show up in a .cpp file
// This is the constructor definition. We have to specify that it's part of the
// The extra "MyClass::" specifies that it's part of the MyClass namespace that we
// declared above.
MyClass::MyClass(const std::string& t_name, void * t_data)
{
// Set our member variables to what was passed in.
this.name = t_name;
this.data = t_data;
}
The above version does exactly the same thing (with some subtle differences between initializer lists and traditional initialization in the constructor that you probably don't care about yet -- again, see other references regarding this if you're curious).
It's pretty obvious that this takes up a lot more space, and that's exactly why the more compressed format is often used. But when you're just starting out, it makes things a lot more clear doing it this way. It's important to understand the difference between a declaration and a definition when it comes to functions (i.e. the constructor), because the example you posted does both at the same time when often they are separated.
I am a beginner in C++ and am trying to learn it by looking at examples.
For whatever reason, this is harder to do in C++ than it is in Perl, Python, Java, or even C. I don't recommend this course; instead, you might invest in a good book.
Here is what I understand:
Let's start with that. I don't think you understand as much as you claim to.
name and *data are variables of the class
No. name and data are variables of the class. Their types are std::string and void*, respectively.
MyClass(...) is the constructor.
Yes.
The meaning of : is the left side class is derived from the right hand side class.
No, : means different things in different contexts. In the context in which it is used in your sample, it indicates that an initializer list follows.
However, what is the meaning of this part of the code:
what are "t_data" and "t_name"?
They are local variables in the constructor function. Specifically, they are the passed-in parameters to the function.
Are they initial values for "data" and "name"?
That is how they are being used in this sample. Specifically, they are being passed to the data and name initializers in the initializer list.
what is the reason t_ is used here?
This is purely the convention of this particular author. I've never seen that convention before.
what is the meaning of : in the above line?
It introduces an initializer list. This construct is only used in constructor member functions. Each named member is initialized by the values listed. (Note: the values do not need to come from parameters -- they can be any legal expression, so: MyClass(int ii) : i(ii), j(cos(0) / 2), k(&global_variable) {} might be a legitmate constructor. )
what is {} at the end of that line?
The body of the constructor member function. Since all of the work of the constructor is being done in the initializer list, the body of the function is empty.

Initializing in constructors, best practice?

I've been programming in C++ a while and I've used both methods:
class Stuff {
public:
Stuff( int nr ) : n( nr ) { }
private:
int n;
}
Or
class Stuff {
public:
Stuff( int nr ) {
n = nr;
}
private:
int n;
}
Note: This is not the same as this, similar but not the same.
What is considered best practice?
Initializer lists are preferred. See FAQ 10.6
One big advantage to using initializers: If an exception is thrown anywhere within the initializer list, the destructors will be called for those members that had already been initialized -- and only for those members.
When you use the contructor body to initialize the object, it's up to you to handle exceptions properly and unwind the object as appropriate. This is usually much harder to get right.
Use the initializer list when possible. For an int, it doesn't matter much either way, but for a more complex member object, you'd end up with the default constructor of the object being called, followed by an assignment to that object, which is likely to end up being slower.
Plus, you have to do it that way anyway for const members or members which don't have a default constructor.
If possible, use the first version.
The first is initializing using intializer lists, and actually calls the constructors of the members.
The second is assignment. If n was of a type with a default constructor, it the would have already been called, and then you'd be assigning to it. If n didn't have a default constructor, you'd be forced to use the first type. Likewise if n was a reference: int &n.
If there are no constructors of you members that directly take one of the parameters to your constructor, it may be worthwhile to add private static functions that can do the conversion for you.
I generally try to do the initializer list when I can. For one thing, this makes it explicit that you are initializing code in the constructor. const memebers have to be initialized this way.
If you just put code in the constructor's body, it is quite possible someone may decide to come along and move a big chunk of it into a non-constructor "setup" routine later.
It can be taken overboard though. I have a coworker who likes to create classes that have 2 pages of initilizer code, no constructor code, and perhaps 2 pages for the entire rest of the class' code. I find that really tough to read.
I want to add that you don't need to declare the initializer list on the Header (.h). It can be done at the implementation of the constructor (which is very common).
So then:
//Stuff.h
class Stuff {
public:
Stuff( int nr );
private:
int n;
}
//Stuff.cpp
Stuff::Stuff(int nr)
: n(nr)
{
//initalize complex members
}
is legal and imo concentrates the initialization of fields where it matters. Sometimes we need to initialize complex members in the body, so you have your initializer list and the complex initialization all in the .cpp file.
The second option is not initialization but assignment. With types that have user defined default constructors, the second option will call the default constructor and later on call the assignment operator (whether user defined or not) to assign the value.
Some types cannot be default initialized: If you have an attribute without default constructor, hold references (constant or not) or have constant attributes they must be initialized in the initializer list.
Arrays can be value-initialized in the initialization list, but not in the constructor body:
class X {
public:
X() : array() {} // value-initializes the array
// equivalent to:
// X() { for ( int i = 0; i < 10; ++i ) array[i]=0; }
private:
int array[10];
};
For POD types, you can value-initialize them in the initialization list but not inside the brackets:
class X {
public:
X() : pod() {} // value-initializes
// equivalent to (but easier to read and subtly faster as it avoids the copy):
// X() { pod = {}; }
private:
PODType pod;
};
Finally, some classes offer functionality through the use of constructors that will be more complex (if achievable) after default construction.
class X
{
public:
X() : v(10) {} // construct a vector of 10 default initialized integers
// equivalent to:
// X() { for ( int i = 0; i < 10; ++i ) v.push_back(0); }
private:
std::vector<int> v;
};
Last, whenever they are in fact equivalent, initialization lists are more idiomatic in C++.