C++ initialization list and constructor - c++

C++14 provides the initialization list and we can use it to initialize elements in a class or struct. What are the differences of the two initialization manners in the following code?
struct MyItem {
MyItem() : val{0} {}
int val;
};
struct MyItem {
MyItem() {}
int val{0};
};

In your case, there is no difference. The first case uses a mem-initializer to initialize val. The second uses a brace-or-equal-initializer. A brace-or-equal-initializer will be used for a member when there is no mem-initializer present for that member. If there is a mem-initializer, it takes precedence, and the brace-or-equal-initializer is ignored.
One can certainly construct contrived examples where there is a difference...
const int i = 42;
struct S1 {
S1(int i): val{i} {} // sets val to the parameter i
int val;
};
struct S2 {
S2(int i) {} // param is ignored
int val{i}; // sets val to 42
};

Related

Can copy elision be perfomed in aggregate initialization in c++17?

Given:
//C++17
#include <string>
struct Foo {
int i;
std::string str;
};
int main() {
Foo foo{1, std::string("Hello, world!")};
}
Can Foo::i and Foo::str be directly initialized from 1 and std::string(...) instead of being copied into them, and explain why can/can't using C++17 standard(probably some code for testing purpose)?
If they can't, how many copies are required?
Aggregate initialization basically performs element-wise copy-initialization. So this:
struct Foo {
int i;
std::string str;
};
Foo foo{1, std::string("Hello, world!")};
does the same initializations as:
int i = 1;
std::string str = std::string("Hello, world!");
And we have a new rule in C++17 that says that:
If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object. [ Example: T x = T(T(T())); calls the T default constructor to initialize x. — end example ]
which means that the second initialization must behave as if you'd written:
std::string str("Hello, world!");
That is, zero copies.
A nice demonstration of the new rule is the following example:
struct X {
X(int ) { }
X(X&& ) = delete;
};
struct Y {
X x;
};
int main() {
Y y{X{4}}; // ill-formed in C++14 due to deleted move ctor
// ok in C++17, no move required
}

Instance variable initialization in C++ private area

In class A,counter b(5); doesn't work.
while counter a; (the call of the default constructor) works.
Why? Is it not possible to call a parameterized constructor for an instance variable?
class counter {
private:
int value;
public:
counter() { this->value = 0; }
counter(int value) { this->value = value; }
};
class A {
private:
// works
counter a;
// works (since C++11)
int x = 5;
// doesn't work
counter b(5);
};
int main()
{
// works
counter myCounter;
// works as well
counter mySecondCounter(5);
return 0;
}
If allowed, counter b(5); technically declares a function named b that returns a counter object by value, and takes a single argument that's an integer.
While that, very likely, would not be the intention of the coder, that's why it is not allowed.
In Bjarne's words about this rule for In-Class Initializers:
We can specify an initializer for a non-static data member in the class declaration. For example:
class A {
public:
int a {7};
int b = 77;
};
For pretty obscure technical reasons related to parsing and name lookup, the {} and = initializer
notations can be used for in-class member initializers, but the () notation cannot.
It is possible. Change
counter b(5);
to
counter b = counter(5);
It is perhaps more elegant to initialise in a constructor intialisation list.
class A {
private:
A() : b(5) {}
counter a;
int x = 5;
counter b;
};
You have four options:
class A {
private:
counter C1{5};
// Works, only with single parameter constructors
counter C2 = 5;
// Works, with any number of parameters
counter C3 = counter(5);
counter C4;
// Constructor initializer list, works with any number of parameters
A() : C4(5) {}
};
In C++, this is not the correct syntax for initializing members with default values.
There are two options to solve it:
1.use operator =
counter b = counter(5);
2.use constructor with initialization list
A() : a(),b(5) {}

Error with implicit copy constructor in struct initialization

I have come up with a problem that I am unable to explain in light of my knowledge of C++.
I have a class that takes a constant reference to a struct of type X but when I pass an argument of type X (which i previously initialized) I get an error from the compiler saying it can't convert to the first member of a member of X. This doesn't make any sense to me.
The error is similar in clang and g++ which leads me to think I am missing something egregious.
error: no viable conversion from 'const Foo::X' to 'Foo::FooTypes'
Why is it trying to convert from X to FooTypes which is the first member of Z?
class Foo {
public:
enum FooTypes {
JPEG
};
struct Z {
FooTypes type;
int a;
};
struct X {
Z caps;
double host_address;
};
Foo(const X& x);
private:
const X x;
};
Foo::Foo(const Foo::X& x) :
x{x} {
}
int main() {
Foo::X new_x = {
{Foo::JPEG, 1}, 1.58 };
Foo *s = new Foo(new_x);
delete s;
return 0;
}
You should use round brackets instead of curly brackets:
...
const X m_x;
};
Foo::Foo(const Foo::X& x) :
m_x(x) {
}
...
here is list initialization:
...
m_x{x.caps, x.host_address}
...
Edit
#PauloNeves
I just found Bjarne Stroustrup's document http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf, which contains next definition:
The general idea of "initializer lists" (as discussed for many years
in EWG) is to allow the use of a brace-enclosed list of expressions in
all contexts that allow initializers. The following list is lifted
from N2532:
Variable initialization; e.g., X x {v};
Initialization of a temporary; e.g, X{v}
Explicit type conversion; e.g. x X{v};
Free store allocation; e.g. p new X{v}
Return value; e.g., X f(){ /* ... */ return {v}; }
Argument passing; e.g., void f(X); /* ... */ f({v});
Base initialization; e.g., Y::Y(v) : X{v} { /* ... */ };
Member initialization; e.g., Y::Y(v) : mx{v} { X mx; /* ... */ };
I think that described Member initialization is your case. So as for me it looks like g++4.9 defect.

How to zero-initialize an union?

Consider the following code:
struct T {
int a;
union {
struct {
int a;
} s1;
struct {
char b[1024];
} s2;
};
};
int main() {
T x = T();
}
Since an explicit constructor is called, the above code ends-up zero-initializing all the data members in x.
But I would like to have x zero-initialized even if an explicit is not called. To do that one idea would be to initialize the data members in their declaration, which seems to be okay for T::a. But how can I zero-initialize all the memory occupied by the union by using
the same criteria?
struct T {
int a = 0;
union {
struct {
int a;
} s1;
struct {
char b[1024];
} s2;
};
};
int main() {
T x; // I want x to be zero-initialized
}
You could zeroize using memset:
memset(&x, 0, sizeof(x));
For a union without a user-defined default constructor, value initialization is zero initialization.
However, zero-initialization of a union may not zero all memory, but only the padding and the first member. If the first member isn't the largest, you could be left with non-zero content.
Since you know that s2 is largest, you can make a default constructor that zeros it:
struct T
{
int a;
union {
int s1a;
char s2b[1024];
};
T() : a(), s2b() {}
};
And now
T x;
will be zeroed.
I would suggest to implement a constructor for T:
struct T {
int a;
union {
struct {
int a;
} s1;
struct {
char b[1024];
} s2;
};
T() : a(), s2() {} // <- proper initialisation
};
In this case, I picked the largest member of your union since it's transparent. Otherwise you could explicitly create the union itself.

initializer_list and default constructor overload resolution

#include <initializer_list>
#include <iostream>
using namespace std;
struct Y {};
struct X
{
X(initializer_list<Y>) { cout << "yay" << endl; }
explicit X() { cout << "boo" << endl; }
};
X f()
{
return {};
}
int main()
{
f();
return 0;
}
This prints out "boo". Why doesn't it print out "yay" ?
Is there anyway to differentiate the following two constructions:
X()
X{}
or
return X();
return {};
or
void g(const X&)
g(X())
g({})
Thanks.
Is there anyway to differentiate the following two constructions:
No. They are not different constructs.
The primary purpose of the {} constructor syntax was to introduce uniform initialization, to have initialization work the same everywhere. If there was a difference between them, it wouldn't be uniform.
If you want to use the empty initializer list constructor, you have to state that you're passing an initializer list explicitly. Like this: return initializer_list<Y>{}; Of course, one of the other purposes of uniform initialization is to not have to type out typenames so much, so you can achieve the same effect with return {{}};
return {}; will always use a default constructor if there is one.
return X({}); or return {{}}; will construct from an empty initialiser list.
It uses the default constructor because list initialization with {} is meant to be a short form of value initialization always, disregarding of other constructors, even if they are initializer list constructors.
Is there anyway to differentiate the following two constructions: ...
The X() is always value initialization, while X{} is only value initialization if X has a default constructor. If it is an aggregate, then X{} is aggregate initialization (initializing the members of X by a {} recursively). If it only has initializer list constructors and no default constructors, then X() is invalid and X{} could be valid
struct A { A(initializer_list<int>); };
A a = A{}; // valid
A b = A(); // invalid
Essentially, what X{} does depends on what X is. But X() always value initializes.
... or return X(); vs return {};
Something subtle to mention... In return {} the target is copy-list-initialized, while in return X(); first direct-initializes an X. But even though it is copy-list-initialized, it can use an explicit default constructor, because value initialization doesn't care about explicit. However when you do return {} and you try to use an explicit non-default constructor you will error out
struct A {
explicit A(initializer_list<int>);
};
A f() { return {}; } // error!
struct B {
explicit B();
};
B g() { return {}; } // OK
You could be a bit more explicit:
return initializer_list<Y>();