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 iterator, tuple 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 constructordecltype(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
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 vector, list, map 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
Name | Category | Description | Github | Wandbox |
---|---|---|---|---|
Example | auto | Usage | source output | source + output |
Example 2 | decltype | Usage | source output | source + output |
Example 3 | declval | Usage | source output | source + output |
Example 4 | nullptr | Usage | source output | source + output |
Example 5 | char16_t,char32_t | Usage | source output | source + output |
Example 6 | constexpr | compile time optimization | source | source + output |
Example 7 | constexpr | constexpr Functions | source output | source + output |
Example 8 | constexpr | constexpr Constructor | source | source + output |
Example 9 | =default | Usage | source | source + output |
Example 10 | =delete | Usage | source output | source + output |
Example 11 | list - initialization | most vexed parsing - problem | source output | source + output |
Example 12 | list - initialization | most vexed parsing - fix | source | source + output |
Example 13 | list - initialization | initialize POD - members | source output | source + output |
Example 14 | list - initialization | initialize POD - constructor | source output | source + output |
Example 15 | list - initialization | initialize_list constructor | source output | source + output |
Example 16 | rvalue reference,move, perfect forwarding | string swap and vector insert without move() | source output | source + output |
Example 17 | rvalue reference,move, perfect forwarding | string swap and vector insert with move() | source output | source + output |
Example 18 | rvalue reference,move, perfect forwarding | perfect forwarding | source output | source + output |
Example 19 | namespace | problem and solution | source | source+output |
Example 20 | namespace | add new types | source | source+output |
Example 21 | namespace | nested | source | source+output |
Example 22 | namespace | versioning | source | source+output |
Example 23 | using | directive | source | source+output |
Example 24 | using | declaration | source | source+output |
Example 25 | using | access non virtual base class members | source | source+output |
Example 26 | using | access base class constructors | source output | source+output |
Example 27 | using | define type alias | source | source+output |
Example 28 | noexcept | Usage | source output | source + output |
Example 29 | override | Usage | source output | source + output |
Example 30 | final | Usage | source output | source + output |
Example 31 | range for loop | Usage | source output | source + output |
Example 32 | static_assert | Usage | source output | source + output |
Example 33 | variadic templates | Pack expansion - smartprint | source output | source + output |
Example 34 | variadic templates | Extract a value from parameter pack | source output | source + output |
Example 35 | variadic templates | Output std::tuple to a stream (operator <<) | source output | source + output |
Example 36 | variadic templates | Tuple implementation | source output | source + output |
No comments:
Post a Comment