{}-Initialization

Contents[Show]

The initialization of variables was uniformed in C++11. The rule is quite simple. {}-Initialization is always applicable. 

Always applicable

For simplicity reasons I will speak in the rest of the post about {}-Initialization, although I mean uniformed inititialization with {}. But before I speak about two important implications of the {}-Initialization in safety-critical software I will show a few special use cases. This uses cases require C++11.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// uniformInitialization.cpp

#include <map>
#include <vector>
#include <string>

// Initialization of a C-Array as attribute of a class
class Array{
  public:
    Array(): myData{1,2,3,4,5}{}    
  private:
    int myData[5];
};

class MyClass{			
  public: 
    int x;
    double y;
};

class MyClass2{		
  public:
    MyClass2(int fir, double sec):x{fir},y{sec} {};
  private: 
    int x;
    double y;
};

int main(){

  // Direct Initialization of a standard container
  int intArray[]= {1,2,3,4,5};   
  std::vector<int> intArray1{1,2,3,4,5};  
  std::map<std::string,int> myMap{{"Scott",1976}, {"Dijkstra",1972}};

  // Initialization of a const heap array
  const float* pData= new const float[3]{1.1,2.2,3.3};

  Array arr;

  // Defaut Initialization of a arbitrary object   
  int i{};                // i becomes 0
  std::string s{};        // s becomes ""
  std::vector<float> v{}; // v becomes an empty vector
  double d{};             // d becomes 0.0
	
  // Initializations of an arbitrary object using public attributes	
  MyClass myClass{2011,3.14};      
  MyClass myClass1= {2011,3.14};    

  // Initializations of an arbitrary object using the constructor
  MyClass2 myClass2{2011,3.14};     
  MyClass2 myClass3= {2011,3.14};   

}

 

First things first. The direct initialization of the C array, the std::vector, and the std::map (line 32 - 34) is quite easy. In case of the std::map the inner {}-pairs are the key and value pairs. The next special use case is the direct initialization of a const C array on the heap (line 36). Special about the array arr  in line 39 is that C arrays can be directly initialized in the constructor initializer (line 10). The default initialization in the lines 42 to 45 looks quite innocent. But if I use round brackets instead of curly brackets, the most vexing parse will happen. That does not sound good. Why? Wait a for the next section. I directly initialize in the line 48 and 49 the public attributes of the objects. It's also possible to call the constructor with curly braces (line 52 and 53).

auto

Always applicable? Yes, but you have to keep a special rule in mind. If you use automatic type deduction with auto in combination with an {}-initialization, you will get a std::initializer_list.

  auto initA{1};          // std::initializer_list<int>
  auto initB= {2};        // std::initializer_list<int>
  auto initC{1, 2};       // std::initializer_list<int>
  auto initD= {1, 2};     // std::initializer_list<int>

This behaviour will change very likely in C++17.

  auto initA{1};          // int
  auto initB= {2};        // std::initializer_list<int>
  auto initC{1, 2};       // error, no single element
  auto initD= {1, 2};     // std::initializer_list<int>

 

To be honest, I don't like this change. The C++11 semantic is quite clear to me. If I use {}-initialization with auto, I will get an initializer list. With C++17 I have to keep the two exceptions with auto in my head.

  1. It makes a difference if you use direct or copy initialization.
  2. It make a difference if you use {}-initialization with one ore more elements.

Most vexing parse

But what does that mean? Seems to be a well-known expression: https://en.wikipedia.org/wiki/Most_vexing_parse.  The story is quickly told. The most C++ developers knows them personally.

At first, a small program that shows the issue.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// parse.cpp

#include <iostream>

struct MyInt{
  MyInt(int i):i(i){}
  MyInt():i(0){}
  int i;
};


int main(){

  MyInt myInt(2011);
  MyInt myInt2();
  
  std::cout << myInt.i << std::endl;
  std::cout << myInt2.i << std::endl;
 
}

 

MyInt in line 5 - 9 is a simple wrapper for the natural number i. The constructor in line 6 sets i to an explicit value. On opposite, the default constructor in line 7 sets i to 0. So far so good. I use in the lines 14 and 15 both constructors and display the values of i. Compile and run, that's all I have to do.

But wait. The program does not compile.

 parse

The error message is not so meaningful. The compiler can interpret the expression in line 15 as a call of a constructor or as a declaration of a function. In case of doubt it interprets the expression as function declaration. The slightly modified program shows it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// parseResolved.cpp

#include <iostream>
#include <typeinfo>

struct MyInt{
  MyInt(int i):i(i){}
  MyInt():i(0){}
  int i;
};

MyInt myFunction();

int main(){

  MyInt myInt(2011);
  MyInt myInt2();
  
  std::cout << typeid(myInt2).name() << std::endl;
  std::cout << typeid(myFunction).name() << std::endl;

}

 

I declare in line 12 the function myFunction that has no arguments and returns a type MyInt. myFunction has the same signature as the function declarations in line 17. Thanks to the typeid operator, the output of the program shows exactly that.

parseResolved

The solutions to the problem is quite easy. If I use curly braces in the lines 12 and 17, the compiler will not interpret both lines as function declaration. I already used this characteristic of the {}-initialization in the first example of this post (line 42 - 45).

But now to the main topic of this post, which is so precious for software in safety-critical systems: preventing narrowing.

Preventing narrowing

Narrowing or more precise narrowing conversion is a implicit conversion of arithmetic values including a loss of accuracy. That sound extremely dangerous.

The following example shows the issue with the classical initialization for fundamental types. It doesn't matter whether I use direct initialization or assignment.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// narrowing.cpp

#include <iostream>

int main(){

  char c1(999);     
  char c2= 999;
  std::cout << "c1: " << c1 << std::endl;
  std::cout << "c2: " << c2 << std::endl;
  
  int i1(3.14); 
  int i2= 3.14;
  std::cout << "i1: " << i1 << std::endl;
  std::cout << "i2: " << i2 << std::endl;

}

 

The output of the program shows two issues. First, the int literal 999 doesn't fit into the type char; second, the double literal doesn't fit into the int type.

narrowingUndefined

That is not possible with {}-initialization.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// narrowingSolved.cpp

#include <iostream>

int main(){

  char c1{999};     
  char c2= {999};
  std::cout << "c1: " << c1 << std::endl;
  std::cout << "c2: " << c2 << std::endl;
  
  int i1{3.14}; 
  int i2= {3.14};
  std::cout << "i1: " << i1 << std::endl;
  std::cout << "i2: " << i2 << std::endl;

}

 

The program is ill-formed. Because of the narrowing with {}-Initialization the compiler has at least to diagnose a warning. Although the program is ill-formed, the compiler has not to reject it.

But now the maximum confusion with GCC begins. It make a difference whether I use the GCC 4.7 or the GCC 4.8. The GCC 4.7 rejected the program; the GCC 4.8 only provides a warning. With GCC 5.1 we get an error. You don't believe me? Try it out with the online compiler https://gcc.godbolt.org/. The clang++ compiler is much more predictable. Therefore my tip. Compile your program in such a way that narrowing is an error. So did I. I used the flag -Werror=narrowing and GCC 4.8 complains about the program.

 narrowing

A small remark at the end. The expression char c3{8} is indeed no narrowing because 8 fits in the type char. The same holds for char c3= {8}.

What's next?

C++11 got with static_assert the the type-traits library two powerful features for checking the source code at compile time. I the next post I will have a deeper look into static_assert and its combination with the functions of the type-traits library.

 

 

 

 

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Get your e-book. Support my blog.

 

Comments   

0 #1 Shafik Yaghmour 2016-10-12 19:14
There are several confused Stackoverflow questions with respect to gcc and clang treating narrowing conversions differently. I discovered when answering this one: http://stackoverflow.com/a/31685448/1708801 that gcc changed to treating narrowing conversions as warnings due to porting issue but this was temporary.

I believe the fact that that a lot of books use the examples form the standard which use the non-normative "error" in comments adds to the confusion. Most users don't understand the distinction between diagnostic, error and warning.
Quote
-3 #2 marriage quotes 2016-10-14 12:51
Hi there friends, how is everything, and what you wish for to say about this article,
in my view its truly amazing in favor of me.
Quote
0 #3 Pacheco 2016-11-11 19:11
This is a good post.
However it you should also alert (hoping you know about it) the problems with "auto" and {}-initialization.

:)
Quote
0 #4 Rainer Grimm 2016-11-12 08:51
Quoting Pacheco:
This is a good post.
However it you should also alert (hoping you know about it) the problems with "auto" and {}-initialization.

:)
Thanks. I will add a few words.
Quote
0 #5 Niklas 2017-03-28 00:41
Excellent site you've got here.. It�s hard to find high-quality writing like yours these days.
I seriously appreciate individuals like you! Take care!!
Quote
0 #6 Raquel 2017-04-07 10:53
I constantly spent my half an hour to read this webpage's articles every day along with a mug of coffee.
Quote

Add comment


My Newest E-Book

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 220

All 257840

Currently are 174 guests and no members online