The following C++20 program is accepted without any warning in all compiles I have tried:
struct A { const int & x = z; int y = x; int z; };
int main()
{
return A{.z=3}.y;
}
https://gcc.godbolt.org/z/nqb95zb7c
But every program returns some arbitrary value. Is it right to assume that this is undefined behavior?
Members are initialized in the order they appear in the class definition, hence the designated initializer is not that relevant, and also this
struct A {
const int & x = z;
int y = x; // <- read of indeterminate value !
int z = 42; // <- doesn't really matter because y is initialized before !
};
int main() {
return A{}.y;
}
is undefined for the same reason.
See also the example from cppreference:
struct A {
string str;
int n = 42;
int m = -1;
};
A{.m=21} // Initializes str with {}, which calls the default constructor
// then initializes n with = 42
// then initializes m with = 21
The example is actually to illustrate something else, but it also shows how members are initialized in order.
Related
I have question on C++ behavior when initializing structures using a list. For example, the following code behaves the same in C and C++. The list initializes x:
struct X {
int x;
};
int main(int argc, char *argv[])
{
struct X xx = {0};
return 0;
}
Now, if I add a constructor, I find out through testing that the constructor is called instead of the simple initialization of the x member:
#include <iostream>
using namespace std;
struct X {
int x;
X(int);
};
X::X(int i)
{
cout << "X(int i)" << endl;
x = i;
}
int main(int argc, char *argv[])
{
struct X xx = {0};
return 0;
}
Output:
X(int i)
Is the behavior of C++ with an identical constructor (as above) to override the simple list initialization? Giving my testing that is what appears to happen.
Thanks!
The following syntax:
X xx = {0};
is just a form of copy-list initialization. This has the effect of invoking the constructor of X, as you observed.
The name of this initialization comes from the fact that this looks like a list is being copied, but just the regular constructor is invoked. Note that this will only consider implicit constructors.
Also the elaborated-type-specifier struct is not necessary in c++, in the declaration of xx.
Note that if you don't provide a constructor such as X(int) (as you did in the first example), then the declaration of xx will instead do aggregate initialization.
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.
This question already has answers here:
What is this weird colon-member (" : ") syntax in the constructor?
(14 answers)
Closed 3 years ago.
I am new to c++ programing.This is an example in Bjarne Stroustrup's c++ book.
Can any one explain to me what this line does
X(int i =0) :m{i} { } //a constructor ( initialize the data member m )
Can anyone tell me what does this ':' symbol does.I am new to c++ programs.
class X {
private: //the representation (implementation) is private
int m;
public: //the user interface is public
X(int i =0) :m{i} { } //a constructor ( initialize the data memberm )
int mf(int i) //a member function
{
int old = m;
m = i; // set new value
return old; // return the old value
}
};
X var {7}; // a variable of type X initialized to 7
int user(X var, X∗ ptr) {
int x = var.mf(7); // access using . (dot)
int y = ptr−>mf(9); // access using -> (arrow)
int z = var.m; //error : cannot access private member
}
Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon.
#include<iostream>
using namespace std;
class Point {
int x;
int y;
public:
Point(int i = 0, int j = 0) : x(i), y(j) {}
/* The above use of Initializer list is optional as the
constructor can also be written as:
Point(int i = 0, int j = 0) {
x = i;
y = j;
}
*/
int getX() const {return x;}
int getY() const {return y;}
};
int main() {
Point t1(10, 15);
cout << "x = " << t1.getX() << ", ";
cout << "y = " << t1.getY();
return 0;
}
/* OUTPUT:
x = 10, y = 15
*/
The above code is just an example for syntax of the Initializer list. In the above code, x and y can also be easily initialed inside the constructor. But there are situations where initialization of data members inside constructor doesn’t work and Initializer List must be used. Following are such cases:
1) For initialization of non-static const data members:
const data members must be initialized using Initializer List. In the following example, “t” is a const data member of Test class and is initialized using Initializer List. Reason for initializing the const data member in initializer list is because no memory is allocated separately for const data member, it is folded in the symbol table due to which we need to initialize it in the initializer list.
Also, it is a copy constructor and we don’t need to call the assignment operator which means we are avoiding one extra operation.
Probably a dumb question, I want to be able to use the passed in A & B values through the default constructor and use them in my functions like get(), but every time i do it gives the value of 0, how do i set the values of A & B and then be able to use them in my other functions. I know i could just pass the value into the get function from the main but i dont want to do it that way.
I have this in the .h,
class example
{
private:
int A, B;
public:
example();
example(int, int);
int get(int, int);
};
then in the .cpp
example::example()
{
cout << hello;
}
example::example(int x, int y)
{
A = x;
B = y;
}
int example::get(int c, int d)
{
int k = c + d / A;
return k;
}
And in the main()
int c = 10;
int d = 12;
int x = 2;
int y = 1;
example obj1(x, y);
example obj2;
int k = obj2.get(c, d);
cout << k;
Im thinking i should see an answer of 10 + 12 / 2 however its 10 + 12 / 0, not sure where i am going wrong if anyone could help thanks!
example(x, y);
does not do anything. It creates a temporary object that goes immediately out of scope. Then
example obj1;
should not compile, as you don't have a default constructor. If it compiles it means that you have provided a default constructor, which is invoked (and which probably doesn't do any initialization). However in your case you want to invoke the constructor example::example(int, int). The correct way of doing it is to define
example obj1(x, y);
I have the following code :
bool c (a == b);
and
bool c {a == b};
where a and b are some variables of same type.
I want to know that, what is the difference in above two initializations and which one should be preferred in what conditions ? Any kind of help will be appreciated.
Both forms are direct initialization.
Using curly braces {} for initialization checks for narrowing conversions and generates an error if such a conversion happens. Unlike (). (gcc issues a warning by default and needs -Werror=narrowing compiler option to generate an error when narrowing occurs.)
Another use of curly braces {} is for uniform initialization: initialize both types with and without constructors using the same syntax, e.g.:
template<class T, class... Args>
T create(Args&&... args) {
T value{std::forward<Args>(args)...}; // <--- uniform initialization + perfect forwarding
return value;
}
struct X { int a, b; };
struct Y { Y(int, int, int); };
int main() {
auto x = create<X>(1, 2); // POD
auto y = create<Y>(1, 2, 3); // A class with a constructor.
auto z = create<int>(1); // built-in type
}
The only drawback of using curly braces {} for initialization is its interaction with auto keyword. auto deduces {} as std::initializer_list, which is a known issue, see "Auto and braced-init-lists".
First one is the C++03 style direct initialization.
The second is C++11 style direct initialization, it additionally checks for narrowing conversions. Herb Sutter recommends the following in new code:
auto c = <expression>;
or when you want to commit to specific type T:
auto c = T{<expression>};
One known drawback with curly braces when T is some class with overloaded constructor, where one constructor gets std::initializer_list as parameter, std::vector for example:
auto v = std::vector<int>{10}; // create vector<int> with one element = 10
auto v = std::vector<int>(10); // create vector<int> with 10 integer elements
Now we have five forms of initializations. They are
T x = expression;
T x = ( expression );
T x ( expression );
T x = { expression };
T x { expression };
Each of the forms has its own peculirities. :)
For example let's assume that you have the following declarations in the global namespace
int x;
void f( int x ) { ::x = x; }
int g() { return x ; }
long h() { return x; }
then in main you can write
int main()
{
int x ( g() );
}
This code will compile successfully.
However a programmer by mistake made a typo
int main()
{
int x; ( g() );
^^
}
Oops! This code also compiles successfully.:)
But if the programmer would write
int main()
{
int x = ( g() );
}
and then make a typo
int main()
{
int x; = ( g() );
^^
}
then in this case the code will not compile.
Well let's assume that the programmer decided at first to set a new value for the global variable x before initializing the local variable.
So he wrote
int main()
{
int x ( f( 10 ), g() );
}
But this code does not compile!
Let's insert equality sign
int main()
{
int x = ( f( 10 ), g() );
}
Now the code compiles successfully!
And what about braces?
Neither this code
int main()
{
int x { f( 10 ), g() };
}
nor this code
int main()
{
int x = { f( 10 ), g() };
}
compiles!:)
Now the programmer decided to use function h(), He wrote
int main()
{
int x ( h() );
}
and his code compiles successfully. But after a time he decided to use braces
int main()
{
int x { h() };
}
Oops! His compiler issues an error
error: non-constant-expression cannot be narrowed from type 'long' to
'int' in initializer list
The program decided to use type specifier auto. He tried two approaches
int main()
{
auto x { 10 };
x = 20;
}
and
int main()
{
auto x = { 10 };
x = 20;
}
and ...some compilers compiled the first program but did not compile the second program and some compilers did not compile the both programs.:)
And what about using decltype?
For example the programmer wrote
int main()
{
int a[] = { 1, 2 };
decltype( auto ) b = a;
}
And his compiler issued an error!
But when the programmer enclosed a in parentheses like this
int main()
{
int a[] = { 1, 2 };
decltype( auto ) b = ( a );
}
the code compiled successfully!:)
Now the programmer decided to learn OOP. He wrote a simple class
struct Int
{
Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x = { 10 };
}
and his code compiles successfully.
But the programmer has known that there is function specifier explicit and he has decided to use it
struct Int
{
explicit Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x = { 10 };
}
Oops! His compiler issued an error
error: chosen constructor is explicit in copy-initialization
The programmer decided to remove the assignment sign
struct Int
{
explicit Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x { 10 };
}
and his code compiled successfully!:)