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.
Related
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.
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.
This is a short question about syntax in c++:
class arrayInit {
public:
bool vars[2];
arrayInit() : vars() {} //1
};
class array {
public:
bool vars[2];
array() {} //2
};
What does 1 and 2 do?
Why they don't zero initialize like this: bool vars[2]={};?
What is the purpose of arrayInit() : and array()? and what is it called so I could search for it?
It's from: https://msujaws.wordpress.com/2010/06/16/initializing-an-array-in-c/
What does 1 and 2 do?
Both 1 and 2 define the default constructor for the respective type
Why they don't zero initialize like this: bool vars[2]={};?
They could if they were using a compiler with c++11 support. Also var() will value initialize the array which is same as vars[2] = {} will explicitly initialize all elements to false
What is the purpose of arrayInit() : and array()? and what is it
called so I could search for it?
They are called the default constructors. C++ compiler will create them for you unless you want to do something special in them. If you were mentioning about what is written beyond the : (colon), that expression is called the initializer list
Read more here
What does 1 and 2 do?
Both allow you to override the default initialization for an array.
InitArray is specifically initializing the vars array with no parameters, I believe it will assume 0 as the default parameter.
Array is not specifically initializing the array, so it is falling back to a default initialization case.
Why they don't zero initialize like this: bool vars[2]={};?
You could do this, this is just another option which encapsulates the bool array in a class to allow you to provide other functionality if you wish.
What is the purpose of arrayInit() : and array()?
If you want default functionality, there is no need to encapsulate the array in its own class. Encapsulation allows you to encapsulate a type to provide different functionality from the default, you could go on to add methods for addition, subtraction, or anything you can think up and have it perform the methods in the way that you specify.
and what is it called so I could search for it?
Good question; Encapsulation, class initialization, array initialization.
http://www.cplusplus.com/doc/tutorial/classes/
Both arrayInit() and array() are default constructors. If the default constructor is missing and other constructors are available, you cannot declare an object from that class type without calling the other constructors e.g. you cannot do this arrayInit arr; without the default constructor.
The part : vars() is called the initialization list. You can read more about them in this link: http://en.cppreference.com/w/cpp/language/initializer_list
I am in the process of learning c++.
I have a struct like this:
struct Info {
const Long rate;
A* ptr;
}
I have a constructor which takes all the arguments as its parameters to initialize the struct. However, this struct is part of another class which will be serialized using boost serialization. In order to serialize that class I would need a default constructor for this struct. However, when I try to write a default constructor such as
Info () {
}
I get an error C2758 that the member rate should be initialized in the constructor.
How to get a default constructor for such a struct which I can use to serialize my class.
You need to initialize the constant value, so:
Info () : rate(0) {
}
The error is probably due to the fact that your Long class does not have a default constructor either.
There are two ways of fixing this:
Add a default constructor to Long, or
Add rate to the initialization list of Info's constructor.
You can see the msdn documentation for C2758 for a description of the error.
In basic term's, a const variable must be initialised in all constructors. The compiler enforces that any built in type or pointer member that is const must be initialised when the object is constructed, as you won't get a chance to give it a meaningful value after construction ( if you could change it after it was created, how is it const ? ).
Also, as a general rule of thumb, it is always a good idea to initialise members that don't have a default constructor ( built in types, pointers, and objects without default constructors ) to something, in all your class constructors. Otherwise they will either be initialised to some random value ( primitives or pointers ), or you will get a compile error ( objects without default constructors ).
Info()
: rate(0)
, ptr(nullptr)
{
}
If you are assigning values to some of your parameters from constructor arguments, don't forget to assign a value to the other members as well.
Info( Long rate)
: rate( rate )
, ptr(nullptr)
{
}
try this :
struct Info {
const Long rate;
A* ptr;
Info():rate(0){} // as Matthew guessed, call the correct Long constructor
// or define a default constructor for Long
};
Can someone please quote an example code when we should not use initialisation list in the constructor and how that can be overcome with assignment?
I am looking for an example for the below statement
This might happen when your class has two constructors that need to initialize the this object's data members in different orders. Or it might happen when two data members are self-referential. Or when a data-member needs a reference to the this object, and you want to avoid a compiler warning about using the this keyword prior to the { that begins the constructor's body (when your particular compiler happens to issue that particular warning). Or when you need to do an if/throw test on a variable (parameter, global, etc.) prior to using that variable to initialize one of your this members.
I believe the main concept that the author of your statement was referring to is the fact that calls made to variables in the initialisation list occur not in the order you see them in the initialisation list, but in the order the variables are listed in the class definition.
That means
if you have two different constructors which use initialisation lists, they must initialise them in the same sequence
your control over sequencing (which may be important if you have mutually-dependent members) is limited
I'd recommend taking a look at Scott Meyer's Effective C++ which covers this (amongst many, many other useful and informative topics).
Here are some examples:
This might happen when your class has two constructors that need to
initialize the this object's data members in different orders.
class Example1 {
public:
Example1(std::string decoded, std::string encoded)
: decoded_(decoded),
encoded_(encoded) {}
explicit Example1(std::string encoded)
: decoded_(), // Can't use "decoded_(Decode())" since "encoded_" isn't initialised
encoded_(encoded) {
decoded_ = Decode(); // Assign here instead of initialising
}
private:
std::string Decode(); // decodes class member "encoded_"
std::string decoded_, encoded_;
};
In this example, decoded_ will always be initialised before encoded_ since that's the order in which they are declared in the class, even if we swap their order in the initialisation list.
Or when a data-member needs a reference to the this object, and you
want to avoid a compiler warning about using the this keyword prior to
the { that begins the constructor's body (when your particular
compiler happens to issue that particular warning).
class Example2 {
public:
Example2() : functor_() {
functor_ = std::bind(&Example2::Do, this);
}
private:
void Do();
std::function<void()> functor_;
};
Here, functor_ needs to use this when it is initialised/assigned. If we were to intialise functor_ in the initialisation list, the this pointer would be referring to an object which at that point wasn't fully initialised. That could be safe depending on the particular circumstances, but the foolproof option is to defer setting functor_ until inside the constructor body, by which point this does refer to a fully-initialised object.
Or when you need to do an if/throw test on a variable (parameter,
global, etc.) prior to using that variable to initialize one of your
this members.
class Example3 {
public:
Example3(int force, int acceleration)
: force_(force),
acceleration_(acceleration),
mass_(0) {
if (acceleration_ == 0)
throw std::exception("Can't divide by 0");
mass_ = force_ / acceleration_;
}
private:
int force_, acceleration_, mass_;
};
Hopefully this is self-explanatory.
I'm not sure what is meant by
when two data members are self-referential
so I can't give an example for that I'm afraid.