Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preferred way of initialization in c++11 [closed]

Tags:

c++

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 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.
like image 457
CrHasher Avatar asked Jul 26 '14 15:07

CrHasher


People also ask

What is initializer list in C++ 11?

Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.

What is an initialization in C?

Initialization is the process of locating and using the defined values for variable data that is used by a computer program. For example, an operating system or application program is installed with default or user-specified values that determine certain aspects of how the system or program is to function.

Which of the following is correct way to initialize integer variable in C?

Different ways of initializing a variable in Cint a, b; a = b = 10; int a, b = 10, c = 20; Method 5 (Dynamic Initialization : Value is being assigned to variable at run time.)

What is the order of initialization for data?

Always write member initializers in a constructor in the canonical order: first, direct base classes in the order in which they appear in the base-specifier-list for the class, then nonstatic data members in the order in which they are declared in the class definition.


3 Answers

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
like image 124
Edward Avatar answered Oct 14 '22 19:10

Edward


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();
like image 30
Deduplicator Avatar answered Oct 14 '22 19:10

Deduplicator


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.

like image 1
shuttle87 Avatar answered Oct 14 '22 18:10

shuttle87