Preferred Initialization Method in C ++ 11

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:

 int i = 0; auto i = 0; 

Why not:

 int i = {0}; auto i = int{0}; 

And in some cases I have to get rid of "=":

 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, 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 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); 

An 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 you do not use curly braces, it is 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. 
+6
source share
7 answers

For something simple, like int in your example, I would agree that

 int i=0; 

probably most commonly understood (among programmers), but there are advantages to using parenthesis initialization, which makes it preferable for me. For instance,

 int i = 3.99; // i gets 3; no warning, no error int i{3.99}; // i gets 3; warning: "narrowing conversion" 

This helps to write more code without errors and, therefore, the best way to do this is in my opinion.

Mixing with auto more dangerous. I usually use auto only for:

  • temporary variable in a loop for a range (e.g. for (const auto &n : mycollection) )
  • to simplify the declaration of the name lambda
  • for iterator instances when I use them explicitly (and not range-for)
  • templated code where this avoids creating a long typedef
+7
source

There are some incorrect derivations:

 auto i{0}; // [comment omitted] int i(); 

The first defines i as std::initializer_list<int> .
The second declares an external function called i that returns int and has no arguments.

Rules of thumb:

  • Use auto where it saves text input, 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 the destination where it works (the potential time mode will be optimized by any current compiler, possibly when lhs and rhs are of different types).

     int i = 0; int* i = 0; // For many types passing `nullptr` is better. 
  • Use generic initializer syntax when assignment does not work.

     std::vector<int> i = {1,2,3}; auto i = new int[]{1,2,3}; 
  • You might want to use the direct constructor call, where at least one obviously non-piggy argument is given to avoid curly braces:

     int i(0); 

Beware that initialization using universal initializer syntax is bad with auto , we get std::initializer_list<> :

 auto i{0}; 

Avoid initializing the old style wherever you pass at least one obvious non-type argument, otherwise you run the risk of inadvertently declaring a function:

 int i(); 
+3
source

I have to say that I'm a little worried about how many people use auto and the potential problems that may arise in the future. I think auto is a great tool in certain situations, for example, when a type name is long and complex (like an iterator), which is hard to make a mistake. But for things like int (where int even shorter than auto ), I feel like we can sacrifice some important type security without a noticeable gain.

For instance:

 #include <iostream> bool contains(const std::string& code) { std::cout << __func__ << ": " << code << '\n'; return true; } bool contains(int32_t value) { std::cout << __func__ << ": " << value << '\n'; return false; } struct user_t { uint32_t id; std::string name; }; int main() { user_t user = {4, "bob"}; // Imagine the original programmer creates somethin like // the following block: { auto i = user.name; if(contains(i)) std::cout << "contained" << '\n'; } // Then, sometime later, another programmer // does some bugfixing/maintenace and accidentally // uses the user id instead of their username: { auto i = user.id; // now calls the wrong function and makes the wrong decision if(contains(i)) std::cout << "contained" << '\n'; } } 

Using auto deprived us of some of the static type checking that we expected from C ++. If the original programmer used std::string i or uint32_t i , then a static type system would protect us at compile time from creating a subtle and hard-to-reach error.

Personally, I would recommend using automatically reasonably for things like short variables in loops. Places where the type is long and complex is difficult for humans. Also the places where the compiler MUST output it (templates?).

For initialization, I would go simple:

 int i = 0; // simple, obvious, hard to improve upon. 
+2
source

For int variables of type i = 0 and i = {0} same. int i = 0 will be the most readable, as this is what people are used to seeing.

If you are going on an auto i{0} walk, you need to know that auto i{0} and auto i = 0 actually define different types. (see Deduplicator 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 

auto c{0} actually creates std::initializer_list<int> , which is almost certainly not what the person who posted the question expected here. Basically, there are a bunch of potentially unpleasant things 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 

Perhaps this is not what you expected. Obviously, if you are writing production code, you would like to choose the best class names, but I was surprised that this did not give any warnings when compiling.

+1
source

I agree with the Deduplicator. I would use:

 int i = 0; 

This is the easiest, smallest, and best known coding method.

0
source

My recommendations:

1) Use auto only for template constructs where you can really benefit from it and for iterators where type names can be long (if you want). Do not use it for primitive types, especially for references. If the function returns a link and you want to use auto, you need to use auto &. If a copy is not made, which can be quite complicated.

2) Think of the code as something you can read on a piece of paper and be frank. Do not try to hide types if they are fixed.

3) The list of initializers can be dangerous. Use them when you need it. I saw many structures that are initialized using initializer lists, and then the developer added another member. With some compilers warnings (bummer) are not issued.

0
source

Use auto name = initializer; exclusively. There are a couple of places where you need auto&& or auto name = (Base*)initializer; . And definitely never use braces, because unified initialization collapses like hell.

-2
source

Source: https://habr.com/ru/post/972885/


All Articles