Using () or {} for initialization

I am a very entry level EE trying to learn C++. I am a working professional (not in school), and trying to teach myself with Bjarne Stroustrup's Programming Principles and Practice second edition. In the third chapter he explains "narrowing conversions". I am familiar with the concept from taking C in college. The book shows an example of narrowing

1
2
  		double x = 2.7;
		int y = x;


He explains that C++11 introduced an initialization notation which outlaws narrowing conversions. It shows the above code should be done this way:
1
2
  		double x{ 2.7 }; // OK
		int y(x); //error:double -> int might narrow 


He later says "So what should you do if you think that a conversion might lead to a bad value? Use {} initializers to prevent accidents".

The verbiage here "bad value" and "prevent accidents" leave me a bit confused on the meaning. The places I would see errors when I took C, many years ago, is like in integer division:
1
2
      double test = 0.0;
      test = 3/2;

In C I would do this to prevent the loss of precision (in this case truncation):
1
2
    double test = 0.0;
    test = (double)3/2;

I thought what the book was telling me was if I used the {} initialization it would prevent assignment or other operations. Initially I thought this:
1
2
3
4
  		double test {1.2};
		cout << "First Line test = " << test << '\n';
		test =  3 / 2 ;
		cout << "Test = " << test << '\n';

would prevent the test =3/2; from truncating. In my mind what I thought was going to happen is the same as the C code I pasted above:
1
2
      double test = 0.0;
      test = (double)3/2;

but that was not the case. I have spent more than a couple hours searching for clarification on the subject, but most answers get deep in the weeds talking about struct's, lists, vectors, and classes. I am at the beginning of this book and all of those things are above my head right now. Also, the book has not covered them yet, and is aimed at beginners. With all that said my question is this:
Obviously I am misunderstanding what the {} initialization accomplishes. Since this is a beginner book, by the person who originally wrote C++, I thought this was an important idea to grasp. Is this in fact a more advanced topic I need to just put on hold and later it will make sense, or am I just not finding a good resource (possibly not asking the right way)?

I know sometimes a book has to tell you to do things, and you have to just take some of it on faith and they will explain it in more depth later on. I also know if you skip more than you should have you will miss half of what you read as a result. Just looking for feedback on this part.
Last edited on
List initialization prevents narrowing conversions as part of initialization,
it doesn't affect anything later on in the program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int x = 999;                    // x is not a constant expression
const int y = 999;
const int z = 99;
char c1 = x;                    // OK, though it potentially narrows (in this case, it does narrow)
char c2{x};                     // error: potentially narrows
char c3{y};                     // error: narrows (assuming char is 8 bits)
char c4{z};                     // OK: no narrowing needed
unsigned char uc1 = {5};        // OK: no narrowing needed
unsigned char uc2 = {-1};       // error: narrows
unsigned int ui1 = {-1};        // error: narrows
signed int si1 =
  { (unsigned int)-1 };         // error: narrows
int ii = {2.0};                 // error: narrows
float f1 { x };                 // error: potentially narrows
float f2 { 7 };                 // OK: 7 can be exactly represented as a float
bool b = {"meow"};              // error: narrows
int f(int);
int a[] = { 2, f(2), f(2.0) };  // OK: the double-to-int conversion is not at the top level 

http://eel.is/c++draft/dcl.init#list-example-14
Hello chrisplusian,

This may be of some help https://www.informit.com/articles/article.aspx?p=1852519 or https://mbevin.wordpress.com/2012/11/16/uniform-initialization/

The narrowing comes in when you try to put the larger "double" int a smaller "int". Since the "int" can only store a whole number the decimal part is dropped.

When you have:
1
2
double test = 0.0;
test = 3/2;

The (3 / 2) is integer division, so there is no decimal value and "test" will have something .0000 in it. Simply you could write: test = 3.0 / 2.0;. I believe that most IDES and compilers consider this number to be a "double" these days. The other option is to write either number as ".0" and the other will be promoted to a "double" B4 the calculation.

The next option is some form of type cast. The old C style type cast is still supported, but from C++11 on "static_cast<double>vnariable or number)" would be the more up to date method.

In my first readings the {}s were called the uniform initializer, but it may also go by other names. Empty the {}s will initialize a variable and the compiler will choose the best form of (0)zero to use. Putting a value inside the {}s will initialize the variable. I can not say that I have heard or read anything that the {}s prevent anything. They just make initialization easier.

In your example:
1
2
3
4
double test{ 1.2 };
cout << "First Line test = " << test << '\n';
test = 3 / 2;
cout << "Test = " << test << '\n';

Line 1 is giving "test" an initial value of (1.2). That is it.

line 3 is still integer division.

Using the {}s in line 1 does not have any affect line 3.

I apologize if I have bee confusing. It is late for me and I do not always get my thoughts together correctly.

Andy
Topic archived. No new replies allowed.