#include <string>
struct T1 { int mem; };
struct T2
{
int mem;
T2() { } // "mem" is not in the initializer list
};
int n; // static non-class, a two-phase initialization is done:
// 1) zero initialization initializes n to zero
// 2) default initialization does nothing, leaving n being zero
int main()
{
int n; // non-class, the value is indeterminate
std::string s; // class, calls default ctor, the value is "" (empty string)
std::string a[2]; // array, default-initializes the elements, the value is {"", ""}
// int& r; // error: a reference
// const int n; // error: a const non-class
// const T1 t1; // error: const class with implicit default ctor
T1 t1; // class, calls implicit default ctor
const T2 t2; // const class, calls the user-provided default ctor
// t2.mem is default-initialized (to indeterminate value)
}
Im currently looking at the reference guide, however there a a few things i don't understand.
I have run the above code, for the struct T2, the data member "int mem" is not in the initialiser list. It is said that t2.mem is default-initialized to an indeterminate value.
But when i run this code, t2.mem seems to be zero initialised for me?
But when i run this code, t2.mem seems to be zero initialised for me?
No, in both cases (T1, T2), mem is not initialized, or initialize by indeterminate value. The explicit declaration of the constructor in this case does not affect the mem initialization. If you want to initialize mem, you must do it explicitly in the member initializer list:
struct T2
{
int mem;
T2() : mem(0) { }
};
Or through the default member initializer:
struct T1 { int mem = 0; };
Or through aggregate initialization ofT1, which is only takes place if T1 does not have any user declared constructors.
struct T1 { int mem; }
int main() {
T1 a{}; // aggregate initialization
assert(a.mem == 0)
T1 b; // default constructor does not value initialize mem
}
If you see that mem initialized by 0 in the second case, then this is most likely a feature of the compiler, or you just get lucky with the 0 value. This is not guaranteed by the standard, don't rely on it.
Related
I know there are quite a few answered questions here about this topic but I couldn't find one that fully answered my question.
Below I'm making some assumptions based on my understanding of initialization (as of C++17). It would be nice if someone could point out / correct my errors.
Given the type
struct A
{
int x;
};
creating a local variable doesn't initialize the object at all
A a; // a.x is indeterminate, accessing it is UB
However, we can enforce aggregate initialization by the following
A a{};
A a = {};
which initializes a.x to 0.
Now, given the type
struct B
{
int x;
std::string s;
};
creating a local variable default initializes the object
B b;
which results in b.s being default initialized (because it's a non-POD) but b.x being indeterminate, accessing it is still UB.
Next, we have
struct C
{
int x = 0;
std::string s;
};
creating a local variable default initializes the object
C c;
which results in C.s being default initialized (because it's a non-POD) and c.x being copy initialized, the behavior is well defined.
Finally, lets compare some types and check, whether they have identical initialization behavior. I'm assuming there is no difference between those types if I create a (default initialized) local variable (A a;).
Case A
struct A1
{
int x;
};
struct A2
{
A2() { }
int x;
};
struct A3
{
A3() = default;
int x;
};
x is never initialized, it's value is indeterminate.
Case B
struct B1
{
int x{};
};
struct B2
{
B2() : x{} { }
int x;
};
struct B3
{
B3() = default;
int x = 0;
};
x is always initialized, it's value is 0.
Case C
struct C1
{
int x;
std::string s;
};
struct C2
{
C2() { }
int x;
std::string s;
};
struct C3
{
C3() = default;
int x;
std::string s;
};
s is always default initialized, but x is never initialized, its value is indeterminate.
Are those statements correct and if not, where are the errors and what is the actual behavior?
You missed just one subtlety: for A3 or C3, writing T3 t{}; or T3() will initialize x to 0 because a default constructor defaulted on its first declaration causes value initialization to zero-initialize the object and default-initialize it.
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
}
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) {}
I have the following piece of code, that behaves as expected on gcc and clang. However, MSVC gives me an unexpected result.
Lets first look at the problematic code.
#include <iostream>
// -----------------------------------------------
class Test // Dummy for MCVE
{
public:
Test();
void Print();
private:
int arr[5];
};
Test tst;
// -----------------------------------------------
template<typename T>
struct range // some stuff not needed by example removed
{
constexpr range(T n) : b(0), e(n) {}
constexpr range(T b, T e) : b(b), e(e) {}
struct iterator
{
T operator*() { return i; }
iterator& operator++() { ++i; return *this; }
bool operator!=(iterator other) { return i != other.i ; }
T i;
};
iterator begin() const { return{ b }; }
iterator end() const { return{ e }; }
private:
T b,e;
};
constexpr range<int> coord(5);
// -----------------------------------------------
Test::Test()
{
for(auto i : coord)
arr[i]=i;
}
void Test::Print()
{
for(auto i : coord)
std::cout << arr[i] << std::endl;
}
// -----------------------------------------------
int main()
{
tst.Print();
}
Now, on both clang and gcc this prints '0 1 2 3 4'
However, on MSVC this prints '0 0 0 0 0'
The reason being that when the constructor on the global variable tst
runs, 'coord' have yet not been initialized (to 0,5), but it is also not random, but rather (0,0).
To me it would make sense that constexpr initialization happens before regular initialization. Is MSVC conformant in this behaviour?
I should perhaps note that I'm using MSVC version 14.0.22823.1, and
that the expected result can be obtained by changing the order of
the declarations
For static storage duration objects initialization must happen in this order:
Zero-initialization.
Constant initialization (i.e. constexpr).
Dynamic initialization.
Relevant standardeese,
C++14 §3.6.2/2:
” Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5)
before any other initialization takes place. […] Constant initialization is performed: […] if an object with static or thread storage duration is initialized by a constructor call, and if the
initialization full-expression is a constant initializer for the object; […] Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place.
The same paragraph defines (breaking the flow of text, so I removed it above)
” A constant initializer for an object o is an expression that is a constant expression, except that it may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.
In the reported example, which I've verified with Visual C++ 2015, the dynamic initialization of the static storage duration object tst takes place before the constant initialization of the static storage duration object coord, and that's a compiler bug.
The C++11 standard 8.5.4.3 says:
"If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized."
struct A
{
int get() { return i; }
private:
int i;
};
int main()
{
A a = {};
int n = a.get();
cout << n << endl;
// n is a random number rather than 0
return 0;
}
Is this a bug of VC++? My VC++ is the latest Nov 2012 CTP.
Value-initialization of a non-aggregate class type is covered by 8.5p8. In your case the (non-union) class has an implicitly-declared defaulted default no-parameter constructor (12.1p5), which is not deleted and is trivial (ibid). Thus the second bullet of 8.5p8 applies:
— if T is a (possibly cv-qualified) non-union class type without a user-provided or deleted default constructor, then the object is zero-initialized and, if T has a non-trivial default constructor, default-initialized;
So A should be zero-initialized, and the program should print 0.
On the following program:
struct A { int get() { return i; } private: int i; };
#include <iostream>
int main() {
char c[sizeof(A)];
new (c) int{42};
std::cout << (new (c) A{})->get() << '\n';
}
gcc-4.7.2 correctly outputs 0; gcc-4.6.3 incorrectly outputs 42; clang-3.0 goes absolutely crazy and outputs garbage (e.g. 574874232).