C++ 11


Distinguish between () and {}


A novel feature of braced initialization is that that it prohibits implicit narrowing conversions among built in types.


double x,y,z;

int sum { x+y+z } // Error, sum of doubles may not be expressible as int

Initilization using parenthesis and “=” does’nt check for narrowing conversions, because that could break too much legacy code.

int sum(x+y+z) // OK

int sum = x+y+z // OK


Another noteworthy characteristics of braced initialization is its immunity to C++’s most vexing parse.


Widget W1(10) ; // Call Widget constructor with argument 10

Widget W2() ;  // Most Vexing parse ! Declares a function named W2 that returns a Widget.!

Widget W3{} ; // Calls Widget Constructor with no arguments.


During constructor overload resolution, braced initializers are matched to std::initlizer_list parameters if at all possible, even if other constructors offer better matches.


class Widget {
public:
Widget(int i, bool b); // ctors not declaring
Widget(int i, double d); // std::initializer_list params
};
Widget w1(10, true); // calls first ctor
Widget w2{10, true}; // also calls first ctor
Widget w3(10, 5.0); // calls second ctor
Widget w4{10, 5.0}; // also calls second ctor



If, however, one or more constructors declare a parameter of type std::initializer_list, calls using the braced initialization syntax strongly prefer the overloads taking std::initializer_lists. Strongly. If there’s any way for compilers to construe a call using a braced initializer to be to a constructor taking a std::initializer_list, compilers will employ that interpretation.


class Widget {
public:
Widget(int i, bool b); // as before
Widget(int i, double d); // as before
Widget(std::initializer_list<long double> il); // added
};

Widgets w2 and w4 will be constructed using the new constructor, even though the type of the std::initializer_list elements (long double) is, compared to the non-std::initializer_list constructors, a worse match for both arguments!

Widget w1(10, true); // uses parens and, as before, // calls first ctor
Widget w2{10, true}; // uses braces, but now calls // std::initializer_list ctor // (10 and true convert to long double)
Widget w3(10, 5.0); // uses parens and, as before, // calls second ctor
Widget w4{10, 5.0}; // uses braces, but now calls // std::initializer_list ctor // (10 and 5.0 convert to long double)


Compilers’ determination to match braced initializers with constructors taking std::initializer_lists is so strong, it prevails even if the best-match std::initializer_list constructor can’t be called.

class Widget {
public:
Widget(int i, bool b); // as before
Widget(int i, double d); // as before
Widget(std::initializer_list<bool> il); // element type is
// now bool
// no implicit
}; // conversion funcs
Widget w{10, 5.0}; // error! requires narrowing conversions

Here, compilers will ignore the first two constructors (the second of which offers an exact match on both argument types) and try to call the constructor taking a std::initializer_list<bool>. Calling that constructor would require converting an int (10) and a double (5.0) to bools. Both conversions would be narrowing (bool can’t exactly represent either value), and narrowing conversions are prohibited inside braced initializers, so the call is invalid, and the code is rejected.

Only if there’s no way to convert the types of the arguments in a braced initializer to the type in a std::initializer_list do compilers fall back on normal overload resolution. For example, if we replace the std::initializer_list<bool> constructor with one taking a std::initializer_list<std::string>, the non std::initializer_list constructors become candidates again, because there is no way to convert ints and bools to std::strings


class Widget {
public:
Widget(int i, bool b); // as before
Widget(int i, double d); // as before
// std::initializer_list element type is now std::string
Widget(std::initializer_list<std::string> il);
// no implicit

}; // conversion funcs
Widget w1(10, true); // uses parens, still calls first ctor
Widget w2{10, true}; // uses braces, now calls first ctor
Widget w3(10, 5.0); // uses parens, still calls second ctor
Widget w4{10, 5.0}; // uses braces, now calls second ctor


Suppose you use an empty set of braces to construct an object that supports default construction and also supports std::initializer_list construction. What do your empty braces mean? If they mean “no arguments,” you get default construction, but if they mean “empty std::initializer_list,” you get construction from a std::initializer_list with no elements.

The rule is that you get default construction. Empty braces mean no arguments, not an empty std::initializer_list:

class Widget {
public:
Widget(); // default ctor
Widget(std::initializer_list<int> il); // std::initializer
// _list ctor
// no implicit
}; // conversion funcs
Widget w1; // calls default ctor
Widget w2{}; // also calls default ctor
Widget w3(); // most vexing parse! declares a function!



If you want to call a std::initializer_list constructor with an empty std::initializer_list, you do it by making the empty braces a constructor argument—by putting the empty braces inside the parentheses or braces demarcating what you’re passing:

Widget w4({}); // calls std::initializer_list ctor
// with empty list
Widget w5{{}}; // ditto


 

An example of where the choice between parentheses and braces can make a significant difference is creating a std::vector<numeric type> with two arguments.




std::vector<int> v1(10, 20);       // use non-std::initializer_list
// ctor: create 10-element
// std::vector, all elements have
// value of 20

std::vector<int> v2{10, 20};       // use std::initializer_list ctor:
// create 2-element std::vector,
// element values are 10 and 20



Prefer nullptr to 0


void f(int); // three overloads of f
void f(bool);
void f(void*);
f(0); // calls f(int), not f(void*)
f(NULL); // might not compile, but typically calls
// f(int). Never calls f(void*)

If NULL is defined to be, say, 0L (i.e., 0 as a long), the call is ambiguous, because conversion from long to int, long
to bool, and 0L to void* are considered equally good


nullptr’s advantage is that it doesn’t have an integral type. To be honest, it doesn’t have a pointer type, either, but you can think of it as a pointer of all types. nullptr’s actual type is std::nullptr_t, and, in a wonderfully circular definition, std::nullptr_t is defined to be the type of nullptr. The type std::nullptr_t implicitly converts to all raw pointer types, and that’s what makes nullptr act as if it were a pointer of all types.

Calling the overloaded function f with nullptr calls the void* overload (i.e., the pointer overload), because nullptr can’t be viewed as anything integral:

f(nullptr); // calls f(void*) overload


For example, suppose you encounter this in a code base:

auto result = findRecord( /* arguments */ );
if (result == 0) {
}

If you don’t happen to know (or can’t easily find out) what findRecord returns, it may not be clear whether result is a pointer type or an integral type.



auto result = findRecord( /* arguments */ );
if (result == nullptr) {
}

there’s no ambiguity: result must be a pointer type.

Prefer const_iterators to iterators


const_iterators are the STL equivalent of pointers-to-const. They point to values that may not be modified. The standard practice of using const whenever possible dictates that you should use const_iterators any time you need an iterator, yet have no need to modify what the iterator points to.


All that changed in C++11. Now const_iterators are both easy to get and easy to use. The container member functions cbegin and cend produce const_iterators, even for non-const containers, and STL member functions that use iterators to identify positions (e.g., insert and erase) actually use const_iterators.



Declare functions noexcept if they won’t emit
exceptions.


int f(int x) throw(); // no exceptions from f: C++98 style
int f(int x) noexcept; // no exceptions from f: C++11 style

If, at runtime, an exception leaves f, f’s exception specification is violated.
·         C++98 exception specification, the call stack is unwound to f’s caller, and, after some actions not relevant here, program execution is terminated.
·         With the C++11 exception specification, runtime behavior is slightly different: the stack is only possibly unwound before program execution is terminated.

In a noexcept function, optimizers need not keep the runtime stack in an unwindable state if an exception would propagate out of the function, nor must they ensure that objects in a noexcept function are
destroyed in the inverse order of construction should an exception leave the function. Functions with “throw()” exception specifications lack such optimization flexibility, as do functions with no exception specification at all.

RetType function(params) noexcept; // most optimizable
RetType function(params) throw(); // less optimizable
RetType function(params); // less optimizable


Use constexpr whenever possible.

constexpr is a feature added in C++ 11. The main idea is performance improvement of programs by doing computations at compile time rather than run time. Note that once a program is compiled and finalized be developer, it is run multiple times by users. The idea is to spend time in compilation and save time at run time. The basic idea of constant expressions is to allow certain computations to take place at compile time--literally while your code compiles--rather than when the program itself is run. This has an obvious performance benefit: if something can be done at compile time, it will be done once, rather than every time the program runs.


Values known during compilation are privileged. They may be placed in read-only
memory,

A function be declared as consexpr
1.     In C++ 11, a constexpr function should contain only one return statement. C++ 14 allows more than one statements.
2.     constexpr function should refer only constant global variables.
3.     constexpr function can call only other constexpr function not simple function.
4.     Function should not be of void type and some operator like prefix increment (++v) are not allowed in consexpr function.

·         constexpr functions can be used in contexts that demand compile-time constants. If the values of the arguments you pass to a constexpr function in such a context are known during compilation, the result will be computed during compilation. If any of the arguments’ values is not known during compilation, your code will be rejected.
·         When a constexpr function is called with one or more values that are not known during compilation, it acts like a normal function, computing its result at runtime. This means you don’t need two functions to perform the same operation, one for compile

Comments

Popular posts from this blog

C++ Guidelines for Multithreaded System

Signalling System #7(SS7) Protocol.

std::shared_ptr