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.
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.
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.
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.)
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.
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:
for (const auto &n : mycollection)
)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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With