New Language Features

Overview
 These are some of the new language features introduced in C++ standards 11 and 14.

Details
auto
Many functions return values or references to class types or arithmetic types. For example, iterators to containers like list, tuple or arithmetic types. Even though the types can be automatically deduced, declaration of the variable with the type is still required as shown below.
double a = 3.14;
int b = 1;
int& c = b; 
int&& e = 1;
std::vector<int>::iterator itr = std::vector<int>().begin();
std::function<void(int)> dblr = [](int n) {return n * 2; }; // explicitly declare lambda function

The auto keyword is remnant of C that was used to declare local variable in a function. In C++ it has a different meaning and is used to assign a data type based on initialized value. The initialized value can also be output of a function such as iteratortuple etc.
auto a = 3.14; // double
auto b = 1; // int
auto* pb = &b; // int*
auto& c = b; // int&
auto d = { 0 }; // std::initializer_list<int>
auto&& e = 1; // int&&
auto&& f = b; // int&

auto g = new auto(123); // int* const auto h = 1; // const int auto i = 1, j = 2, k = 3; // int, int, int auto l = 1, m = true, n = 1.61; // error -- `l` deduced to be int, `m` is bool auto o; // error -- `o` requires initializer
auto f = 0.f; //float
std::vector<int> ivec;
auto itr = ivec.begin(); //std::vector<int>::iterator

It can be used to declare an instance of lambda function and its output as shown below.
template<typename T, typename U>
auto adder = [](T n, U v)->auto {return n+v;};

//prints:12.5
cout << adder<int,double>(10,2.5) << endl;
The example depicts the usage.

decltype
decltype is reverse of auto where a data type can be deduced from a variable or an expression. decltype can also be used specify return types in template functions.  
decltype is reverse of auto where a data type can be deduced from a variable or an expression. 
auto i = 0; //int
decltype(i) &i2 = i; //reference to int
int a = 1; // a is declared as type int
decltype(a) b = a; // decltype(a) is int
const int& c = a; // c is declared as type const int&
decltype(c) d = a; // decltype(c) is const int&
decltype(123) e = 123; // decltype(123) is int
int&& f = 1; // f is declared as type int&&
decltype(f) g = 1; // decltype(f) is int&&
decltype((a)) h = g; // decltype((a)) is int&

decltype can also be used specify trailing return types in template functions as shown below. Note that the return type must be declared as auto.
template <typename U, typename V>
auto add(U u, V v)->decltype(u+v) { return u+v; };//return sum
The example 2 depicts the usage.

declval
It's a template based helper function, defined as below.
Syntax
template <class T>  
typename add_rvalue_reference<T>::type declval()

It returns an rvalue reference to type T without referring to any object, making it possible to use member functions in the operand of the decltype specifier without the need to go through constructors. T may be an incomplete type.
This function shall only be used in unevaluated operands (such as the operands of sizeof and decltype).
struct Default
{
    int foo() const { return 1; }
};
 
struct NonDefault
{
    NonDefault() = delete;
    int foo() const { return 1; }
};

//Example
decltype(Default().foo()) n1 = 1;         // type of n1 is int
//  decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
decltype(std::declval<NonDefault>().foo()) n2 = n1;
The example 3 depicts the usage.

The size of long is not consistent across platforms. In linux, it's 64 bits and in Windows it's 32 bits.The new data type long long is a 64 bits across all the platforms.

nullptr_t data type is introduced specifically to prevent misuse of NULL in place of null pointers. nullptr is the only instance of this type and can be used in place of a null pointer.

char16_t and char32_t were introduced to provide uniformity among other things. The size of char16_t data type is fixed at 16 bits and the size of char32_t data type is fixed at 32 bits across all the platforms. char16_t is used for UTF-16 encoding and char32_t is used for UTF-32 encoding.

The keyword constexpr means constant expression. Like const, it can be applied to variable. Also, unlike const, constexpr can also be applied to functions and class constructors. constexpr indicates variable value, or return value, which is constant and, where possible, is computed at compile time.

=default In C++ 11, a class can define following special member functions.

  • default constructor
  • copy constructor
  • copy assignment operator
  • destructor 
  • move constructor 
  • move assignment operator 
If it's missing, the compiler conditionally generates some of these functions. Sometimes it's desirable to explicitly generate these functions, when it's not generated by the compiler using keyword =default. 

It's also possible to suppress conditional generation of the above functions by using keyword =delete. It can be also used to suppress implicit conversions in user defined functions.

The C++11 standard library introduced a new template based type initializer_list to handle variable-length {}-lists. initializer_list is used to aid uniform initialization. It's automatically created when braces {} are used to initialize.

list initialization enables uniform way of initializing using a pair of braces {}. Arithmetic types such as int, double etc are implicitly initialized. When used with structs or classes the compiler generates code to call appropriate constructor.

When using containers such as lists, vectors, maps, a lot of temporary objects are created and destroyed during the life time of an application. To overcome this, rvalue reference and move semantics are introduced. An rvalue reference behaves just like an lvalue reference except that it can bind to a temporary value.

move semantics takes advantage of the new rvalue reference to optimize temporary object creation and its reuse.

The rvalue semantics enable passing the arguments without ambiguity to the handler function in the factory method.

Before C++11, universal reference was used to bind to both rvalue and lvalue. An universal reference is formed by making const reference of some type.

In large projects, multiple classes can share same name, which can lead to compilation errors and confusion. namespace can be used to modularise them in separately to avoid overlapping.

typedefs are commonly used provide an alias to a type. However it has some limitations when used in templates. In C++ 11, using keyword is introduced to overcome these limitations also extend its functionality.

noexcept
During compilation, the  compiler adds extra code to functions to handle stack unwinding in case of an unhandled exception. However this may not be desirable in every case. 
noexcept specifier determines whether a function could throw exceptions
Syntax
//noexcept specifier
noexcept 
//noexcept operator
noexcept (expression)
In case of  noexcept operator, the expression is evaluated during compile time, can be a boolean returning true or false
noexcept is equivalent to noexcept (true)true means  the function will not not throw exception and  the compiler will not to add stack unwinding code. In case any exception is still thrown, the application is aborted.  
This can be clearly seen in the example 28 and its console output.
The exception is caught in case of bar() but application got terminated in case of foo<int>(). The compiler also emitted warnings against potential crash.

override
In a multi class hierarchy, virtual functions enable overriding base class functionality by derived classes. However sometimes it can be confusing and often mistakes can happen in a large project if the signatures don't match in the derived classes. 
As shown in the example below, there is a slight change in the signature of the print function compared to the same function in the base class, leading to chaos as it's not detected. 
struct X
{
    virtual void print(int){ }
};

struct Y: public X
{
    void print(double){ }
};

    Y y;
    (dynamic_cast<X*>(&y))->print(1.0); //calls X::print(int)

The suffix override is designed to overcome this. 
The suffix override can be applied to derived class functions. It does not affect generate generation. It When the code compiles, the compiler flags an error if the derived class function has different signature as the base class virtual function. 
struct X
{
    virtual void print(int){}
};

struct Y: public X
{
    void print(double) override {}
};
//compiler error
The example 29 depicts the usage.

final
Sometimes it's desirable to prevent overriding a virtual function or derivation from a class itself. The suffix final is designed to handle it.
final keyword indicates compiler that a virtual function of a base class cannot be overridden. in derived classes. This is shown below.
struct X
{
    virtual void print(int) final {}
};

struct Y: public X
{
    void print(int) {}
};
//compiler error print() cannot be overridden

Similarly a class cannot be derived from it if it's defined as final. This is shown below.
struct X final
{
    virtual void print(int) final {}
};

struct Y: public X
{
    
};
//compiler error struct X cannot be derived
The example 30 depicts the usage.

range for loop 
for loops are versatile can be used in various scenarios. When used in conjunction with containers vectorlistmap  etc. or memory arrays, it provides only indexing using either iterators or integers. 
To access the underlying data structure inside these containers or arrays, an explicit temporary variable needs to be created. Also the range should be explicitly mentioned. The example below depicts.
int intarray[] = {1,2,3,4,5};
for (int i=0,len=sizeof(intarray)/sizeof(int); i<len; ++i)
    cout << intarray[i];
cout << endl;
map<int,string> ismap {{65,"A"},{66,"B"},{66,"C"},{67,"D"}};
for (auto itr = ismap.begin(); itr != ismap.end(); ++itr)
    cout << itr->first << "  " << itr->second << endl;

C++11 introduces a new form of for loop, which iterates over all elements of a given range, array,
or collection.  The general syntax is as follows:
Syntax
//decl: A declaration of a variable that maps to items in the range. e.g., int i or auto i.
//coll: The container such as array, vector, list, map,  initializer list etc. 
//statement: Any statement (typically a compound statement)
for ( decl : coll ) {    statement   }

The same for loop from above can be rewritten as below using range based for loop.
int intarray[] = {1,2,3,4,5};
for (auto &i:intarray)
    cout << i;
cout << endl;
map<int,string> ismap {{65,"A"},{66,"B"},{66,"C"},{67,"D"}};
for (auto &i : ismap)
    cout << i.first << "  " << i.second << endl;
The example 31 depicts the usage.

static_assert
Macros such as assert() can be used in debug builds to check for errors during runtime to break when an error condition becomes true. For example,  
assert(ptr == nullptr)
During execution, the assert macro evaluates if ptr is nullptr. If so, either debugger is  launched or the terminate() is called. Note during release build runs, these checks are not made.

C++ 11 introduces a new keyword static_assert() to detect errors during compilation.
Syntax
static_assert(bool_condition, error_message)
When the  bool_condition becomes false, compilation is stopped and error_message is displayed. 

This is depicted in the example below. In the first swap() call, the condition is true, i.e., 8 > 1 hence the compilation continues. In  the second swap() call, the condition becomes false, i.e., 1 > 8  hence compilation is stopped.
template <typename T, typename T2>
void  swap(T src, T2 dst)
{
    static_assert((sizeof(T2) > sizeof(T)), "overflow");
    dst = src;
}
    char ch = 'a';
    double d;
    swap(ch, d);  //OK
    swap(d, ch);  //Compilation Error
 This is shown in the example 32.

With the variadic templates feature, it's possible to define template based class or function that can have any number (including zero) of parameters. To accomplish this, a new kind of parameter called parameter pack is introduced that can singly represent a list of zero or more parameters in the templates definition. A template class or function with at least one parameter pack is called a variadic template.

Summary of Examples
NameCategoryDescriptionGithubWandbox
Example autoUsagesource    outputsource + output
Example 2decltypeUsagesource    outputsource + output
Example 3declvalUsagesource    outputsource + output
Example 4nullptrUsagesource    outputsource + output
Example 5 char16_t,char32_tUsagesource    outputsource + output
Example 6constexprcompile time  optimizationsourcesource + output
Example 7constexprconstexpr Functionssource    outputsource + output
Example 8constexprconstexpr Constructorsourcesource + output
Example 9=defaultUsagesourcesource + output
Example 10=deleteUsagesource     outputsource + output
Example 11list - initializationmost vexed parsing - problemsource    outputsource + output
Example 12list - initializationmost vexed parsing - fixsourcesource + output
Example 13list - initializationinitialize POD - memberssource    outputsource + output
Example 14list - initializationinitialize POD - constructorsource    outputsource + output
Example 15list - initializationinitialize_list constructorsource    outputsource + output
Example 16rvalue reference,move,
perfect forwarding
string swap and
vector insert  without move()
source    outputsource + output
Example 17rvalue reference,move,
perfect forwarding
string swap and vector insert with move()source    outputsource + output
Example 18rvalue reference,move,
perfect forwarding
perfect forwardingsource    outputsource + output
Example 19namespace problem and solutionsourcesource+output
Example 20namespace add new typessourcesource+output
Example 21namespace nestedsourcesource+output
Example 22namespace versioningsourcesource+output
Example 23using directivesourcesource+output
Example 24using declarationsourcesource+output
Example 25using access non virtual base class memberssourcesource+output
Example 26using  access base class constructorssource    outputsource+output
Example 27using define type aliassourcesource+output
Example 28noexceptUsagesource    outputsource + output
Example 29overrideUsagesource    outputsource + output
Example 30finalUsagesource    outputsource + output
Example 31range for loopUsagesource    outputsource + output
Example 32static_assertUsagesource    outputsource + output
Example 33variadic templatesPack expansion - smartprintsource    outputsource + output
Example 34variadic templatesExtract a value from parameter packsource    outputsource + output 
Example 35variadic templatesOutput std::tuple to  a stream (operator <<)source    outputsource + output
Example 36variadic templatesTuple implementationsource    outputsource + output

No comments:

Post a Comment