Why is int x{ y = 5 } possible?
The operator=()
results in a value, which is the value assigned to the variable. Because of this, it is possible to chain assignments like this:
int x, y, z;
x = y = z = 1;
I will start from your last question
Also, why doesn't the compiler or IDE complain about main() not returning an int?
According to the C++ Standard (6.6.1 main function)
5 A return statement in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling std::exit with the return value as the argument. If control flows off the end of the compound-statement of main, the effect is equivalent to a return with operand 0 (see also 18.3).
And relative to this question
How is this possible, since y = 5 is not a calculable expression?
From the C++ Standard (8.18 Assignment and compound assignment operators)
1 The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand.
Sp this declaration
int x{ y = 5 };
can be equivalently split into two statements
y = 5;
int x{ y };
Moreover in C++ you can even to make a reference to the variable y the following way
int &x{ y = 5 };
Here is a demonstrative program
#include <iostream>
int main()
{
int y;
int &x{ y = 5 };
std::cout << "y = " << y << '\n';
x = 10;
std::cout << "y = " << y << '\n';
}
Its output is
y = 5
y = 10
You may this declaration
int x{ y = 5 };
rewrite also like
int x = { y = 5 };
However take into account that there is a difference between these (looking similarly as the above declarations) two declarations.
auto x{ y = 5 };
and
auto x = { y = 5 };
In the first declaration the variable x
has the type int
.
In the second declaration the variable x
has the type std::initializer_list<int>
.
To make the difference more visible see how the values of the objects are outputted.
#include <iostream>
int main()
{
int y;
auto x1 { y = 5 };
std::cout << "x1 = " << x1 << '\n';
auto x2 = { y = 10 };
std::cout << "*x2.begin()= " << *x2.begin() << '\n';
std::cout << "y = " << y << '\n';
return 0;
}
The program output is
x1 = 5
*x2.begin()= 10
y = 10
How is this possible, since y = 5 is not a calculable expression?
It is an assignment, and assignments yield values, i.e. the "cv-unqualified type of the left operand", see [expr.ass/3]. Hence y = 5
results in y
, which is 5
, which is used to initialize x
.
With respect to your second question, see cppreference on main (or [basic.start.main/5]):
The body of the main function does not need to contain the
return
statement: if control reaches the end of main without encountering areturn
statement, the effect is that of executingreturn 0;
.
Hence, compiler or IDE warning you about a missing return
statement at the end of main
would be plain wrong. Admittedly, the fact that you should always return
objects from non-void
functions execpt main
is kind of... well, for historical reason I guess.