Is it possible in the following example to call the "not default constructor" of class A for every element of mVector within the constructor of class B ?
class A {
public:
A (int n) {/*stuff*/}
};
class B {
public:
B (): mVector(10) {} //call A(int n) constructor?
private:
vector<A> mVector;
};
If you want to set all the elements to the same value, there's a constructor for that
mVector(10, 42) // 10 elements initialised with value 42
If you want to set the elements to different values, use list initialisation
mVector{1,2,3,4,5,6,7,8,9,10} // 10 elements with different values
Strictly speaking, this doesn't do exactly what you describe; it creates a temporary T, and then uses that to copy-initialise each vector element. The effect should be the same, unless your type has weird copy semantics.
You could do one thing here:-
B() : mVector(10, A(10))
{
}
Or
B() : mVector(10, 10)
{
}
Both are essentially the same thing. However, former one is more efficient.
You can use the constructor overload of std::vector taking an element count and a value which for your use case is equivalent to:
std::vector(size_type count, const T& value);
Use it to initialize the elements with the value type's non default constructor:
std::vector<A> mVector(10, A{0}); // 10 elements copy initialized using 'A{0}'.
Or when initializing in the initialization list:
B() : mVector(10, A{0}) {}
Related
I have an object which is non-copyable and which requires an argument to its contructor:
class Foo
{
public:
Foo() = delete;
Foo(const Foo &) = delete;
Foo(int x);
private:
int member;
};
Foo::Foo(int x) : member(x)
{
}
I have another class which contains as a member an array of such objects:
class Bar
{
public:
Bar(int a);
private:
Foo members[4];
};
Suppose that my object Foo is really far too big and its constructor far too complicated for a temporary or duplicate copy to ever exist or the constructor to ever be called more than once for each item of the array.
How do I write the constructor for the containing class to pass the arguments to the items in the member array?
I have tried:
Bar::Bar(int a) : members { a, a+1, a+2, a+3 }
{
}
g++ says "use of deleted function Foo::Foo(const Foo&)".
[Edit] I have also tried:
Bar::Bar(int a) : members { {a}, {a+1}, {a+2}, {a+3} }
{
}
as suggested by Yksisarvinen. This also says "use of deleted function Foo::Foo(const Foo&)", but only if I declare a destructor for Foo. Without a destructor this compiles correctly. How do I get it to compile for a class which has a destructor?
Three options here, depending on what you actually want to do:
1. Use C++17
C++17 has guaranteed copy elision, which will solve the problem.
2. Provide a move constructor
Compiler chooses copy constructor, because by declaring your own copy constructor you prevented possibility to generate a move constructor. Depending on your actual class, move constructor may or may not be viable.
Foo(Foo &&) = default; //or implement "stealing" of the resource here
Note that by the rule of five you should also provide copy/move assignment operators and destructor (defaulted or not).
3. Wrap each array element in braces
If neither of above options is valid, the workaround is to simply wrap each of the array elements in its own set of braces
Bar::Bar(int a) : members { {a}, {a+1}, {a+2}, {a+3} }
In general, your previous option relied on conversion from int to Foo. Once the compiler does the conversion, it has to copy (or move, but you excluded that) the converted object to array, and that triggers an error. By wrapping them in braces, the elements are initialized as Foo objects (and not as int to be converted to Foo), and no move/copy is needed.
I have read that std::vector always initializes it's objects with their default values say for an int it is 0. The same should be applicable even for classes where the default constructor is called. However the results shown by a test program are a bit different :-
#include <iostream>
#include <vector>
using namespace std;
class A
{
public:
int i;
A(){};
};
class B
{
public:
int i;
B() = default;
};
template <typename T>
void seev (const vector<T> &v)
{
for (auto &x:v)
{
cout << x.i << ' ';
}
cout << '\n';
}
int main()
{
vector<int> vi(5); // just to show that std::vector always default initializes values & hence int will be 0 here
vector<A> va(5);
vector<B> vb(5);
for (auto x: vi)
cout << x << ' ';
cout << '\n';
seev (va);
seev (vb);
return 0;
}
The output is :-
0 0 0 0 0
8854016 0 8847696 0 8854016
0 0 0 0 0
My question is why was the value of member i undefined for A & not for B ? What difference did the constructor signatures :-
A() {}
&
B() = default;
make ?
There is a difference between default initialization and value initialization. The difference is shown for B but not for A:
When a class has a non-= defaulted constructor, this constructor is called when either default or value initialization is used. Its responsibility is to initialize all members appropriately.
When a class either has no constructor or an = defaulted default constructor the initialization of the members depends on how the object is constructed: when initializing the object without parenthesis default initialization is done which leaves subobjects without a default constructor uninitialized. When initializing the object with parenthesis value initialization is done which value initializes all subobjects. Value initialization of built-in types mean they get a suitable zero value while default initialization of built-in types means they are left uninitialized.
Since A has an explicitly written default constructor, its constructor needs to initialize all members which aren't of class type with a default constructor. B has an implicitly written default constructor and default or value initialization is performed as necessary for the subobjects.
The objects in a std::vector<T> are constructed using T() (if no other arguments are provides as is the case, e.g., for push_back() or emplace_back()). For A members that means they are left uninitialized, for B members that means they are zero initialized.
When creating a vector with a specific size, the vector doesn't set default values, it uses something called value initialization which is something completely different.
For primitive types, like e.g. int, that means the value will be zero-initialized (i.e. zero). But for objects with constructors the default constructor (if it has one) will be used.
In the case of your A class, the default constructor does not initialize the i member variable, so it will be uninitialized and have an indeterminate value, by printing that value you have undefined behavior.
As for the B class it's a POD type which means that value initialization of the whole object will also value-initialize all members. B is a POD type because it has a trivial default constructor (which A does not have).
A() {};
You are not initializing A::i in A() so its value is unspecified after value initialization. This behaviour is different to what you would get with a compiler-provided default constructor.
A() = default;
Defining as default on the other hand has the effect of providing a constructor with the same semantics of the compiler-synthesized default constructor. That is to say, A::i would get value-initialized (and therefore zero-initialized) when an A object is value initialized with expressions such as A() or A{}.
Note: this can be fixed by either dropping the definition of the default constructor, define it as default, or explicitly initialize i.
struct A
{
// Compiler provided A() will initialize i
// when A is value initialized
int i;
};
or, equivalently in terms of initialization semantics,
struct A
{
int i;
A() = default; // useful if other constructors defined
};
An "empty" constructor will not initialize anything, so the fact that va contains zero is just luck/coincidence. The default constructor will do nothing either, so they will produce the same thing. What you are seeing is undefined behaviour (or unspecified behaviour perhaps - as we shouldn't really expect world war three to break out because of an uninitialized variable).
The problem is that std::vector is supposed to call the default constructor & not set the values by default. The default constructor of int sets it's value to 0 hence you get a std::vector<int> with all values initialized with 0. However you default constructor of A is empty & hence it doesn't initialize i with any value. If you define the constructor as :-
A() { i = 0; }
Then your vector would definitely initialize the i of the objects of A with 0. What default does is this :-
#include <iostream>
#include <vector>
using namespace std;
class A
{
public:
A()
{
cout << "Default constructed A\n";
}
A (A &obj)
{
cout << "Copy constructed A\n";
}
};
class X
{
int x;
double d;
bool b;
A obj;
public:
X() = default;
void show()
{
cout << fixed << x << ' ' << d << ' ' << b << '\n';
}
};
int main()
{
cout << boolalpha;
vector<X> v(1);
v[0].show();
return 0;
}
/* Output :-
Default constructed A
0 0.000000 false
*/
What B() = default; roughly means as far I get from this test code that it is a replacement for :-
B()
{
i = 0;
d = 0.00;
b = false;
// obj is default initialized
}
ie the initialization of the data members with their default values (for primitives) or default constructors (for classes). But this is only for runtime initialization which means for locally created objects the definition is same as B() = {}.
So the thing was that class A had a user defined constructor (with an empty body) & hence didn't initialize because that's how the programmer defined it. On the other hand class B had a constructor that was left on to the compiler to call implicitly. Hence for local objects it didn't do any specific initialization but for dynamic objects (like that of a std::vector) it did the value type initialization.
Consider the following class:
Class A
{
public:
A() = delete;
A(const int &x)
:x(x)
{}
private:
int x;
};
How can one create an std::vector<A> and give an argument to the constructor A::A(const int&)?
How can I create a std::vector of type A and give an argument to A's constructor?
std::vector<A> v1(10, 42); // 10 elements each with value 42
std::vector<A> v2{1,2,3,4}; // 4 elements with different values
How would I add 3 to the vector?
v.emplace_back(3); // works with any suitable constructor
v.push_back(3); // requires a non-explicit constructor
The lack of a default constructor only means you can't do operations that need one, like
vector<A> v(10);
v.resize(20);
both of which insert default-constructed elements into the vector.
Templates are not instantiated in one go : they only instantiate what is needed. A satisfies all the conditions for the following (constructing an empty vector) to be valid :
std::vector<A> v;
However, as A does not have a default constructor, the following (creating a vector with default-initialized content) would fail :
std::vector<A> v(100);
And that's a good thing. However, valid methods will be instantiated fine :
v.emplace_back(42);
The trick is in how you add elements into the vector and what member functions of the vector you use.
std::vector<A> v;
v.emplace_back(3);
I have seen people put a parenthesis after the member variable in the initialization list. I wonder why would people do that?
For example, I have a STL container in header file:
class A{
public:
A();
...
private:
vector<string> v;
}
and in source file:
A::A() : v() {}
My question is what is v() and why do people do that since that doesn't look like v is initialized into a value either
That will run the default constructor or initializer (for plain types) for the member. In this context, it will default construct the vector. Since it is the default constructor, it is not necessary here. v would have been default constructed in the absence of an initializer.
class Example {
private:
int defaultInt;
vector<int> defaultVector;
int valuedInt;
vector<int> sizedVector;
public:
Example(int value = 0, size_t vectorLen = 10)
: defaultInt(), defaultVector(), valuedInt(value), sizedVector(vectorLen)
{
//defaultInt is now 0 (since integral types are default-initialized to 0)
//defaultVector is now a std::vector<int>() (a default constructed vector)
//valuedInt is now value since it was initialized to value
//sizedVector is now a vector of 'size' default-intialized ints (so 'size' 0's)
}
};
For kicks and giggles, you could also do thirdVector(vectorLen, value) to get a vector with vectorLen elements with the value value. (So Example(5, 10) would make thirdVector a vector of 10 elements valued 5.)
My question is what is v() and why do people do that since that doesn't look like v is initialized into a value either
This is sometimes done to be more explicit. For non POD types this is not necessary as the default constructor is automatically called for them. If the types default constructor has not been defined or is not accessible, this will cause a compile error.
This makes the most sense for POD types, as their value is undefined when they are not initialized.
struct A
{
int t;
A() : { /* value of t undefined */ }
}
struct A
{
int t;
A() : t() { /* value of t is t's default value of 0 */ }
}
I have a class field which is a std::vector. I know how many elements I want this vector to contain: N. How do I initialize the vector with N elements?
std::vector has a constructor declared as:
vector(size_type N, const T& x = T());
You can use it to construct the std::vector containing N copies of x. The default value for x is a value initialized T (if T is a class type with a default constructor then value initialization is default construction).
It's straightforward to initialize a std::vector data member using this constructor:
struct S {
std::vector<int> x;
S() : x(15) { }
}
class myclass {
std::vector<whatever> elements;
public:
myclass() : elements(N) {}
};
All the constructors that allow you to specify a size also invoke the element's constructor. If efficiency is paramount, you can use the reserve() member function to reserve the size. This does not actually create any elements, so it is more efficient. In most cases, though, supplying the size through the vector constructor is just fine.