I'm somewhat new to c++ programming. I couldn't find my answer any where on google so hopefully it can be answered here.
is there a difference between the following
unsigned int counter{ 1 };
or
unsigned int counter = 1;
the book uses the first option and its confusing to me because it doesn't explain the difference. below is the following code from the book i'm following.
#include <iostream>
#include <iomanip>
#include <cstdlib> // contains function prototype for rand()
using namespace std;
int main()
{
for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
cout << setw(10) << (1 + rand() % 6);
// if counter is divisible by 5, start a new line of output
if (counter % 5 == 0) {
cout << endl;
}
}
}
Yes, they are two different types of initialization in C++.
The first one, i.e., unsigned int counter{ 1 }; is a direct initialization.
Whereas, the second one, i.e., unsigned int counter = 1;, is a copy initialization.
For all the details you can directly refer to the documentation.
However, I can emphasize:
Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.
Initialization references here
For unsigned int type (such in your case), there are no real differences between the two initializations.
Note
The usage of curly braces in the first statement (unsigned int counter{ 1 }) provides an additional constraint:
Otherwise (if T is not a class type), if the braced-init-list has only one element [...], T is direct-initialized [...], except that narrowing conversions are not allowed.
In other words, the usage of curly braces in the initialization does not allow data looseness.
That is:
unsigned int counter{ 12.3 }; // error!!!
won't compile because you are trying to initialize an integer with a floating-point value.
Note this is a "property" of curly braces in the initialization. It is not strictly related to the initialization type.
In fact, you can also write:
unsigned int counter = { 12.3 }; // error!
which is, instead, a copy initialization, but having curly braces does not allows narrowing conversions.
Adding to the other explanations above.
I would use the first option(unsigned int counter{ 1 };) to initialize a list of values for a variable and for a single value, I would prefer to use the second option(unsigned int counter = 1;)
Hope this helps.
Consider the following demonstrative program.
#include <iostream>
struct A
{
int x;
explicit A( int x = 0 ) : x( x ) {}
};
int main()
{
A a1 { 10 };
std::cout << "a1.x = " << a1.x << '\n';
// A a2 = { 10 };
}
In this declaration
A a1 { 10 };
there is used the direct initialization.
And in the commented declaration
// A a2 = { 10 };
that can be also rewritten like
// A a2 = 10;
there is used the copy-initialization. But the constructor declared with the specifier explicit. So the compiler will issue an error. That is it is unable to convert the integer object 10 to the type of A implicitly.
You could write instead
A a2 = A{ 10 };
That is calling the constructor explicitly.
Of course for fundamental types there is no difference because neither constructor is applied except that narrowing conversion is not allowed when the braced initialization is used as for example
int x { 1.0 };
There is a big difference when the type specifier is the placeholder auto.
For example
auto x = 10;
x has the type int.
auto x { 10 };
x again has the type int.
auto x = { 10 };
Now x has the type std::initializer_list<int>.
For example you could rewrite your loop
for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
the following way
for ( auto counter{ 1u }; counter <= 20; ++counter) {
but you may not write
for ( auto counter = { 1u }; counter <= 20; ++counter) {
because in this case the type of the variable counter is std::initializer_list<unsigned int>.
So in general you have the following forms of initializations
T x = value;
T x = { value };
T x( value );
T x { value };
For example
#include <iostream>
int main()
{
int x1 = 1;
std::cout << "x1 = " << x1 << '\n';
int x2 = ( 2 );
std::cout << "x2 = " << x2 << '\n';
int x3( 3 );
std::cout << "x3 = " << x3 << '\n';
int x4{ 4 };
std::cout << "x4 = " << x4 << '\n';
}
The program output is
x1 = 1
x2 = 2
x3 = 3
x4 = 4
But there is one more situation when instead of T() you should use T{} as an initializer. It is when template functions are used.
Consider the following demonstrative program
#include <iostream>
template <class>
void f()
{
std::cout << "f<T>() is called\n";
}
template <int>
void f()
{
std::cout << "f<int>() is called\n";
}
int main()
{
f<int()>();
f<int{}>();
}
Its output is
f<T>() is called
f<int>() is called
The construction int() used as a template argument specifiers the type template argument int while the construction int{} used as a template argument specifiers a non-type template argument of the type int equal to 0.
unsigned int counter = 1 ;
This style of initialization is inherited from C language.
unsigned int counter {1} ;
This style of initialization is of C++.
The difference is if you provide a wrong value while using C++ style initialization
for example:
unsigned int counter {-1} ;
This will give error (use -std=c++11 to compile it)
But this will not give any error.
unsigned int counter = -1 ;
Related
It is stated on the site cppreference.com, something like that
For each declarator, the initializer may be one of the following:
( expression-list ) (1)
= expression (2)
{ initializer-list } (3)
comma-separated list of arbitrary expressions and braced-init-lists in parentheses
But in my code
int main(){
int a,b=5,c(a,b);
return 0;
}
when I try to compile, the following error occurs
...error: expression list treated as compound expression in initializer [-fpermissive]
My question is, if list of multiple expressions is allowed in such style of initialization, then why the compiler is not accepting it with variable c?
What am I missing?
All right, let's look at this:
int main(){
int a,b=5,c(a,b);
return 0;
}
What do you expect c(a,b) to actually do?
Let's simplify this just slightly:
int main(){
int a,b=5;
int c(a,b);
return 0;
}
This will generate the same syntax error, but it now stands alone. So...
Your code would work if there were a constructor for int that took two ints as parameters. This would also compile:
int c(int a, int b);
But in that case, you're actually defining a function.
Also, this works:
int main() {
int a = 5;
int b = 10;
int c(b);
std::cout << "C == " << c << std::endl;
}
That works because an int can be initialized from a single int. But you're getting an error because you can't initialize an int from two other ints.
This works:
#include <iostream>
class MyClass {
public:
MyClass(int a, int b): value(a + b) {}
int value;
};
int main() {
int a = 5;
int b = 10;
MyClass c(a, b);
std::cout << "C == " << c.value << std::endl;
}
And maybe that's what the article you read was trying to tell you. Note: cpppreference is NOT a good site for learning C++. Get a good book.
There are quite a few posts on SO with similar titles, but they seem to be triggered by various syntactic errors and I didn't see a consistent pattern yet..
using namespace std;
class A
{
public:
A(int a_) : a(a_) {}
int a;
};
int main()
{
A x{3};
A y{0};
if ((y=x).a)
cout << y.a << endl;
int i = 1;
if (int j = i)
cout << j << endl;
if ((A z = x).a) // error: expected primary-expression before ‘z’
cout << z.a << endl;
(int m = 1); // error: expected primary-expression before ‘int’
}
Am I wrong to assume A z = x is an assignment expression, which should have the same value as z?
Am I wrong to assume A z = x is an assignment expression
Yes, you are wrong. There is no assignment going on here. The = in this statement represents initialization, not assignment. The statement A z = x; defines the variable z, where z is constructed from x. The copy constructor is used here, not copy assignment. It is a declaration statement, not an expression statement.
Your confusion is reasonably common, and it is made worse by the fact that the condition in an if statement can be a declaration of a single non-array variable with a brace-or-equals initializer. Syntactically, a declaration with an "equals" initializer can look a lot like an assignment. A big difference, as you discovered, is that you cannot treat the declaration as a sub-expression. The condition is either a declaration or an expression, not a mix of both.
The good news is that C++-17 added an optional init-statement to the if statement syntax. So what you appear to want would be achieved by the following.
if ( A z = x; z.a ) // Semicolon separates init-statement from condition
cout << z.a << endl;
// At the end of the `if` statement, `z` goes out of scope.
You can't declare a variable in an if statement in that fashion. The declaration has to be of the form:
if (X x = y) ... (or if (auto x = y) ...)
However, you can still achieve what you are trying to do if you provide a suitable conversion operator in class A, like this:
#include <iostream>
using namespace std;
class A
{
public:
A(int a_) : a(a_) {}
int a;
operator bool () { return a != 0; }
};
int main()
{
A x{3};
const A &y = x;
if (y.a)
cout << y.a << endl;
if (A z = x)
cout << z.a << endl;
}
Live demo
Consider the following snippet:
#include <iostream>
union U{
U(): i(1) {}
int i;
int j = 2; // this default member initializer is ignored by the compiler
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
The code prints (see live example):
1
1
Where in the Standard does it say that the default member initializer for the member U::j is ignored by the compiler?
Note that the union below doesn't compile and this is OK according to [class.union.anon]/4. I was thus expecting the snippet above also not to compile.
See live example:
union U{
int i = 1;
int j = 2;
};
Where in the Standard does it say that the default member initializer for the member U::j is ignored by the compiler?
See [class.base.init] paragraph 9 bullet 9.1 in the C++17 CD.
N.B. your demo has undefined behaviour, because the active member of the union is i but you read from j. This works with some compilers as a non-standard extension, but is not allowed in ISO C++.
Note that you are declaring a union object, where all members share the same memory area - member variables turn into different "typed views" of the same data.
Thus, as members i and j are effectively stored in the same memory location, any initialization you perform on j (with the initializer) will be overwritten by your constructor setting i.
Just for test, remove the initialization of i from the constructor:
#include <iostream>
union U{
U() {}
int i;
int j = 2; // this initializes both i & j
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
The output would be
2
2
Update:
As per #Ayrosa comments and being just intrigued, I modified the original snippet to perform some initialization with a function (instead of a constant) to induce side effects.
#include <iostream>
int static someStatic()
{
std::cout << "Initializer was not ignored\n";
return(2);
}
union U{
U(): i(1) {}
int i;
int j = someStatic(); // this default member initializer is ignored by the compiler
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
Result was:
1
1
Meaning that the call to someStatic() was, in fact, ignored by the compiler.
Consider following example:
#include <iostream>
struct Test
{
int a, b;
Test(int, int)
{
a = b = 0;
}
};
int main() {
Test v {1, 1};
std::cout << v.a << ' ' << v.b << '\n';
return 0;
}
Here {1, 1} calls the Test(int, int) constructor and thus output is 0 0 .
Is it possible to force compiler to aggregate initialize v so that output would be 1 1?
At this moment I use separate method to initialize fileds directly, like:
struct Test
{
...
static Test NewTest(int x, int y)
{
Test ret;
ret.a = x;
ret.b = y;
return ret;
}
}
But I wonder if there a nicer way to do it.
Aggregate initialization requires the type to be an aggregate. Having a user-declared constructor makes the type a non-aggregate, so no this isn't possible. If you remove the constructor then your type will become an aggregate, and aggregate-initialization will occur.
Is it possible to force compiler to aggregate initialize v so that output would be 1 1?
One way to do that would be to remove the constructor completely.
struct Test
{
int a, b;
};
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Improve this question
int i = 0; // (a) Old C style should I use it?
int i{0}; // (b) Brace direct init
int i{}; // (c) Same as (b)
int i = {0}; // (d) as (b)
int i = {}; // (e) as (c)
auto i = 0; // (f) auto = int in this case.
auto i = int{0}; // (g) auto = more specific.
auto i = int{}; // (h) same as above (g)
Which one to use?
Sutter says use:
int i = 0;
auto i = 0;
Why not:
int i = {0};
auto i = int{0};
And should I get rid of "=" in some cases:
int i{0};
auto i{0}; // i is not what some might expect in this case. So I would prefer using "=" everywhere possible like int i = {0}; ...
EDIT:
This is what I'm aiming for it seems to me the most consistent:
rectangle w = { origin(), extents() };
complex<double> c = { 2.71828, 3.14159 };
mystruct m = { 1, 2 };
int a[] = { 1, 2, 3, 4 };
vector<int> v = { 1, 2, 3, 4 };
point p = {}; // Default initializes members
int i = {0}; // Checked assembly for this and it's binary the same as int i{0}; could be written also as int i = {};
string s = {""}; // Same as string s = {}; (OR) string s;
Real life examples:
std::string title = { pt.get<std::string>("document.window.title") };
const std::string file = { R"(CoreSettings.xml)" };
int_least64_t currentTick = { 0 }; // (OR) int_least64_t currentTick = {};
bool isRunning = { false }; // (OR) bool isRunning = {};
App* app = { nullptr }; // (OR) App* app = {};
Event event = {};
double detectedFrameRate = { 1000000000.0 / (swapIntervalDeltaCumulative / 20.0) };
double precision = { static_cast<double>(boost::chrono::high_resolution_clock::period::num)
/ boost::chrono::high_resolution_clock::period::den };
auto timeSpan = boost::chrono::duration_cast<boost::chrono::nanoseconds>(nowTime - startTime);
Alternative would be:
std::string title { pt.get<std::string>("document.window.title") };
const std::string file { R"(CoreSettings.xml)" };
int_least64_t currentTick { 0 }; // (OR) int_least64_t currentTick{};
bool isRunning { false }; // (OR) bool isRunning{};
App* app { nullptr }; // (OR) App* app{};
Event event {};
double detectedFrameRate { 1000000000.0 / (swapIntervalDeltaCumulative / 20.0) };
double precision { static_cast<double>(boost::chrono::high_resolution_clock::period::num)
/ boost::chrono::high_resolution_clock::period::den };
auto timeSpan = boost::chrono::duration_cast<boost::chrono::nanoseconds>(nowTime - startTime);
If not using braces it's ugly or error-prone:
int_least64_t currentTick = 0; // C style - changed this from double to int recently and compiler did not complain so I had something like int_least64_t currentTick = 0.0; ugly!
bool isRunning = false; // C style
App* app = nullptr; // C mixed with C++11 style;
Event event; // might not be initialized by all compilers
int someInt = func(); // func() returns double no error but narrowing.
For something simple, such as the int in your example, I'd agree that
int i=0;
is probably the most commonly understood (among programmers), but there are advantages to using the brace-initialization that, to me, make it preferable. For instance
int i = 3.99; // i gets 3; no warning, no error
int i{3.99}; // i gets 3; warning: "narrowing conversion"
It helps to write more bug-free code and is therefore a better way to do it in my view.
Mixing it with auto is more perilous. I typically use auto only for:
the temporary variable in a range-for loop (e.g. for (const auto &n : mycollection))
to simplify declaration of a named lambda
for iterator instances when I use them explicitly (rather than range-for)
templated code where doing so avoids creating a lengthy typedef
There are some wrong derivations:
auto i{0}; // [comment omitted]
int i();
The first one defines i as a std::initializer_list<int>.
The second declares an extern function named i returning int and having no arguments.
Rules-of-thumb:
Use auto where it saves typing and the type or its behavior is obvious. Examples:
auto x = new mymegathingy;
auto y = container.begin();
auto z = filestream.seekoff(0, basic_ios::curr);
Use assignment where that works (A potential temporary will be optimized away by any current compiler, possible when lhs and rhs have different types).
int i = 0;
int* i = 0; // For many types passing `nullptr` is better.
Use universal initializer syntax where assignment does not work.
std::vector<int> i = {1,2,3};
auto i = new int[]{1,2,3};
You might want to use direct constructor call where at least one obviously non-type argument is given, to avoid curly braces:
int i(0);
Beware that initializing with universal initializer syntax mixes bad with auto, we get a std::initializer_list<>:
auto i{0};
Avoid old-style init wherever you do not pass at least one obvious non-type argument, otherwise you risk inadvertently declaring a function:
int i();
For the int type variables i = 0 and i = {0} are the same. int i = 0 will be the most readable as this is what people are used to seeing.
If you do go down the auto route you need to be aware of the fact that auto i{0} and auto i = 0 are actually defining different types. (see Deduplicator's comment)
See this code:
#include <iostream>
#include <typeinfo>
int main(){
auto a = 0;
std::cout << "a is of type:" << typeid(a).name() << std::endl;
auto b = int{0};
std::cout << "b is of type:" << typeid(b).name() << std::endl;
auto c{0};
std::cout << "c is of type:" << typeid(c).name() << std::endl;
}
When we run this we get:
a is of type:i
b is of type:i
c is of type:St16initializer_listIiE
The auto c{0} is actually creating a std::initializer_list<int> which is almost certainly not what was expected here by the person who posted the question.
Basically there are a bunch of potentially nasty things here when it comes to readability.
Here's something I just compiled with g++ -Wall -std=c++11 main.cpp (g++ version 4.7.2)
#include <iostream>
#include <typeinfo>
#include <vector>
class d{
public:
std::vector<int> v;
d(std::initializer_list<int> l) : v(l) {
std::cout << "constructed class d with a " << l.size() << "-element list\n";
}
};
int main(){
auto d{0};
std::cout << "d is of type:" << typeid(d).name() << std::endl;
}
When we run this we get:
d is of type:St16initializer_listIiE
This might not be what you expected either. Clearly if you are writing production code you would want to choose better class names, but I was surprised this gave no warnings when being compiled.
I agree with Deduplicator.
I would use:
int i = 0;
It is the simplest, smallest, and most well known way of coding.
My recommendations are:
1) Use auto only for template constructions, where you can actually benefit from it and for iterators where type names may be long (if you prefer). Do not use it for primitive types, especially not references. If a function returns a reference and you want to use auto, you need to use auto&. If a not a copy is made, which can be quite tricky.
2) Think of code as someting that you can read on a piece of paper and be explicit about it. Do not try to hide types if they are fixed.
3) Initializer list can be dangerous. Use them when you need it. I have seen many structures, which are initialized using initializer lists and then a developer have added another member. With some compilers, no warnings are thrown (bummer).
Use auto name = initializer; exclusively. There are a couple places where you need auto&& or auto name = (Base*)initializer;. And definitely do not ever use the curly braces because uniform initialization is broken as hell.