C++11(formerly known as C++0x) is the latest standard in C++ programming language approved by ISO on 12 August 2011. The last standard was C++03. The naming convention of specification follows the year in which specification was published.
C++11 includes several new features to the core language and extends the C++ standard library. The principles used for enhancingC++ to C++11 are mainly
- Maintain backward compatibility with C++03 and with C.
- The most preferred style of introduction of new features is through standard library instead of through core language.
- Improve C++ to help systems and library implementations rather than useful to only to a specific application.
- Improve typesafety by providing safe alternate techniques to previous unsafe techniques.
- Used "zero-overhead" design principle. Any Additional support required by some utilities must be used only if utility is used.
- Make C++ easy to teach and learn, there by eliminating the need of any utility used expert programmers.
- null pointer constant
- In class member initialisation
- Delegating constructors
- Override
- Final
- static_assert
- Type traits
- auto
- decltype
- Lambdas
- rvalue references and Move semantics
- Variadic templates
- std::function
- std::bind
- std::tuple and std::tie
- std::initializer_list
- Uniform initilization
- Suffix return type syntax
- Explicit conversion operators
- default and delete
- Enum class, scoped and strongly typed enums
- constexpr - Generalised and guaranteed constant expression
- Raw string literals
- User defined literals
- Range based for loops
- Right angle brackets
- Control and query object alignment
- Wrapper references
- std::unique_ptr
- std::shared_ptr
- std::weak_ptr
- std::string
- std::vector
- STL
- std::thread
- std::mutex
- std::guard_lock and std::mutex
- std::async
- std::future and std::promise
- null pointer constant
void foo(char*);
void foo(int);
Now the statement
will always call foo(int) and this is not what the programmer wants to do.
C++11 solves this problem by introducing a new keyword "nullptr" which is of type "std::nullptr_t". Now the statement
foo(nullptr);
will certainly call "foo(char*)". Also
int* i = nullptr; // Ok
bool b = ch; // Ok. b is false
int x = nullptr; // error
foo(nullptr); // call foo(char*) instead of foo(int)
- In class member initialisation
| C++03 | C++11 |
class A {
public:
A()
: m_a(4)
, m_b(5)
, m_s1("string1")
, m_s2("string2")
{}
A(int a)
: m_a(a)
, m_b(5)
, m_s1("string1")
, m_s2("string2")
{}
A(string s)
: m_a(4)
, m_b(5)
, m_s1(s)
, m_s2("string2")
{}
private:
int m_a;
int m_b;
string m_s1;
string m_s2;
};
|
class A {
public:
A(){}
A(int a)
: m_a(a)
{}
A(string s)
: m_s1(s)
{}
private:
int m_a = 4;
int m_b = 5;
string m_s1 = "string1";
string m_s2 = "string2";
};
|
- Delegating constructors
Consider the following example
class A {
public:
A()
{
init(45);
}
A(int a)
{
init(a);
}
private:
void init(int a)
{
m_a = a;
}
int m_a;
};
In C++11 we would have been
class A {
public:
A()
: A(45)
{
}
A(int a)
: m_a(a)
{
}
private:
int m_a;
};
- Override
class A {
public:
virtual void foo(float);
};
class B : public A {
public:
virtual void foo(int);
};
The foo is intended to override the base class method, but since it has a different signature it will create a different virtual method. C++11 provides us with new syntax to avoid this problem.
class A {
public:
virtual void foo(float);
};
class B : public A {
public:
virtual void foo(int) override; // error, because it does not override a method in base class
};
The "override" identifier will make compiler to search for a method with exact same signature in base class, if it cannot find such a method than it will give an error.
- Final
| Java | C++11 |
final class Base1{}
class Derived1 extends Base1 {} // Error: Base1 has been marked as final
class Base2 {
public final void foo(){};
}
class Derived2 extends Base2 {
public void foo(){} // Error: foo() has been marked as final
}
|
struct Base1 final {};
struct Derived1 : Base1 {}; // Error: Base1 has been marked as final
struct Base2 {
virtual void foo() final;
};
struct Derived2 : Base2 {
virtual void foo(); // Error: foo() has been marke das final
};
|
- static_assert
The syntax is static_assert(constant-expression, error-message);
For example
int main(int argc, const char * argv[])
{
uint64_t i = 0;
foo(i);
return 0;
}
template <typename T>
void foo(T t)
{
static_assert(sizeof(T) == 16, "T must be of size 16 bytes");
// do something
}
{
uint64_t i = 0;
foo(i);
return 0;
}
The output from clang for above code will be
Test.h:17:5: error: static_assert failed "T must be of size 16 bytes"
- Type traits
#include <type_traits>
using namespace std;
struct Foo {};
struct FooBar {
virtual ~FooBar(){}
};
struct FooBarBar : FooBar {};
struct A {
private:
A(A& o);
A& operator=(A& O)const;
};
int main(int argc, const char * argv[])
{
cout<<"float has virtual destructor "<<has_virtual_destructor<float>::value<<endl;
cout<<"float is polymorphic "<<is_polymorphic<float>::value<<endl;
cout<<"Foo has virtual destructor "<<has_virtual_destructor<Foo>::value<<endl;
cout<<"Foo is polymorphic "<<is_polymorphic<Foo>::value<<endl;
cout<<"FooBar has virtual destructor "<<has_virtual_destructor<FooBar>::value<<endl;
cout<<"FooBar is polymorphic "<<is_polymorphic<FooBar>::value<<endl;
cout<<"FooBarBar has virtual destructor "<<has_virtual_destructor<FooBarBar>::value<<endl;
cout<<"FooBarBar is polymorphic "<<is_polymorphic<FooBarBar>::value<<endl;
cout<<"uint8_t is signed "<<is_signed<uint8_t>::value<<endl;
if (is_copy_constructible<A>::value && is_copy_assignable<A>::value) {
cout<<"A is copyable "<<endl;
}
return 0;
}
Output:
float has virtual destructor 0
float is polymorphic 0
Foo has virtual destructor 0
Foo is polymorphic 0
FooBar has virtual destructor 1
FooBar is polymorphic 1
FooBarBar has virtual destructor 1
FooBarBar is polymorphic 1
uint8_t is signed 0
- Auto
The compiler automatically infers about the correct data type. It increases readability and verbosity of programme.| C++03 | C++11 |
void foo(const std::unordered_map<int, int>& map) {
std::unordered_map<int, int>::const_iterator it = map.begin();
std::unordered_map<int, int>::const_iterator end = map.end();
for (; it != end; ++it) {
// other stuff
}
}
|
void foo(const std::unordered_map<int, int>& map) {
auto it = map.begin();
auto end = map.end();
for (; it != end; ++it) {
// other stuff
}
}
|
- decltype
| C++03 | C++11 |
{ float a = 4.5; const float b = 6.5; const float& c = a; float array[5]; float *ptr; float local1; float local; float local3; float& local4 = a; const float local5 = 1.5; const float& local6 = b; float local7[5]; float& local8 = a; float& local9 = a; return 0; } |
int main(int argc, const char * argv[])
{
float a = 4.5;
const float b = 6.5;
const float& c = a;
float array[5];
float *ptr;
decltype(a) local1;
decltype(1.5) local2;
decltype(2.5+3.8) local3;
decltype(a=1.5) local4 = a; //there is no assignment a to 1.5, a == 4.5 as before
decltype(b) local5 = 1.5;
decltype(c) local6 = b;
decltype(array) local7;
decltype(array[3]) local8 = a;
decltype(*ptr) local9 = a;
return 0;
}
|
- Lambdas
The following example show the usage of lambdas
#include <algorithm>
#include <vector>
#include <iostream>
bool sortFunction(const int& a, const int& b)
{
return a < b;
}
struct SortClass {
bool operator()(const int& a, const int& b) const
{
return a < b;
}
} sortObject;
void print(const std::vector<int>& vector)
{
auto it = vector.begin();
auto end = vector.end();
for (; it != end; ++it)
std::cout << *it << " ";
std::cout << std::endl;
}
int main(int argc, const char * argv[])
{
std::vector<int> v0 = {99, 54, -25, 51, -11};
std::vector<int> v1 = {-6, -45, 0, 67, 31};
std::vector<int> v2 = {34, -12, 44, 12, -87};
std::vector<int> v3 = {56, 48, -9, 36, -55};
std::sort(v0.begin(), v0.end()); // using default comparison i.e operator <
std::sort(v1.begin(), v1.end(), sortFunction); // using comparision function
std::sort(v2.begin(), v2.end(), sortObject); // using comparison object
std::sort(v3.begin(), v3.end(), [](const int& a, const int& b){ // using comparison lambda
return a < b;
});
std::cout << "sorted v0 = ";
print(v0);
std::cout << "sorted v1 = ";
print(v1);
std::cout << "sorted v2 = ";
print(v2);
std::cout << "sorted v3 = ";
print(v3);
return 0;
}
The output is
sorted v0 = -25 -11 51 54 99
sorted v1 = -45 -6 0 31 67
sorted v2 = -87 -12 12 34 44
sorted v3 = -55 -9 36 48 56
A lambda can access the local variables in the scope in which it is used using the syntax []. To access the local variables by reference &variableName is user and to access a local variable by value =variableName is used. To access all local variable by reference & is used and to access all local variables by value = is used.
void f()
{
int x = 9;
int y = 45;
[&x](){ x += 5;}(); // Lambda can only access x by refernce, now x = 14
/* After the execution of lambda x = 14 here because we have accessed x by reference in lambda */
}
void f()
{
int x = 9;
int y = 45;
[&](){
x += 5;
y += 3;
}(); // Now lambda can access all local variables by reference
/* value of x = 14 and y = 48 because we have accessed x and y by reference inside lambda*/
int main(int argc, const char * argv[])
{
std::function<int (int)> factorial = [&factorial](int i) {
return i <= 1 ? 1 : i * factorial(i -1);
};
int x = factorial(4); // x = 24
return 0;
}
- rvalue references and Move semantics
A major performance problem in C++03 is costly and unnecessary deep copies that happen implicitly when an object is passed by value.
For example lets suppose std::vector is internally a traditional C-style array with a size. Now if such std::vector is created or returned from a function, it cane done by creating a new std::vector and copying all of rvalue data into it. Then temporary and all of its data is destroyed(For simplicity ignore return value optimisation used by some compilers).
template <typename T>
class Vector {
public:
Vector(); // Default constructor
Vector(const Vector&); // Copy constructor
Vector(Vector&&); // Move constructor
Vector& operator=(const Vector&); // Copy assignment operator
Vector& operator=(Vector&&); // Move assignment operator
virtual ~Vector(); // Destructor
private:
T* data;
size_t size;
};
template<typename T>
Vector<T>::Vector()
: data(nullptr)
, size(0)
{
}
template<typename T>
Vector<T>::Vector(Vector<T>&& o)
: data(o.data)
, size(o.size)
{
o.data = nullptr;
o.size = 0;
}
template<typename T>
Vector<T>::~Vector()
{
if (data)
delete[] data;
}
typedef Vector<float> Matrix;
Matrix operator*(const Matrix& a, const Matrix& b)
{
Matrix result; // Suppose a.data = 0x003423ab
// Matrix multiplication algorithm
return result; //Vector(Vector&&) is called
// C.data = result.data and c.size = result.size
// result.data = nullptr and result.size = 0
} //~Vector()
// delte result.data, okay since it is already nullptr
int main(int argc, const char * argv[])
{
Matrix a;
Matrix b;
Matrix c = a * b;
// c.data = 0x003423ab, due to move semantic of rvalue reference
// a deep copy operation is being avoided
return 0;
}
In order to get an rvalue reference, the function template std::move<T>() can be used.Another example where a rvalue reference and move is used is the swap problem, traditionally a swap method will look like
template<typename T>
void swap(T& t1, T& t2)
{
T temp(t1);
t1 = t2;
t2 = temp;
}
However using move we can achieve an almost perfect swap
template<typename T>
void swap(T& t1, T& t2)
{
T temp = std::move(t1);
t1 = std::move(t2);
t2 = std::move(temp);
}
- Variadic templates
template<typename... Args> class A;
The above template class will take any number of typenames as its template arguments. To create the instance of above class with three type arguments.
A<int, char, unsigned> a;
The number of arguments can be zero so A<> a; can also work well. If we do not want template to take zero number of arguments we have to modify our template definition
template<typename First, typename... Args> class A;
Variadic template can also be applied to functions as well, they not only allow type-safety to variadic functions(like printf), but also allow printf functions to take and process non-trival objects.
template<typename... Params>
void printf(const std::string& formatString, Params... params);
Please note the use of ellipsis operator (...). It has two forms. First when it appears on left side of parameter, it declares a parameter pack. Using parameter pack user can bind zero or more arguments to the variadic template parameters. Parameter pack can also be used for non-type parameters. Second when ellipsis operator occurs on the right side of template or function call argument, it unpacks the parameter pack into separate arguments like args... in the body of printf(see below). In other words use of ellipsis operator in code causes whole expression that precedes the ellipsis to repeat for every subsequent argument unpacked from argument pack, and all these expressions will be separated by comma.The use of variadic template is often recursive. The variadic template parameters are not available to the implementation function or class. Therefore the implementation of a typical C++11 style printf would be like
void printf(const char* s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("Invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cout << value;
printf(s + 1, args...);
return;
}
}
std::cout << *s++;
}
throw std::logic_error("Extra arguments provided");
}
This is recursive template, please not that variadic template version of printf calls itself recursively and when args... is empty calls base case.There is no simple mechanism to iterate over through the values of variadic templates. There are few ways to translate argument pack into a single argument use. If we intended to do so then this relays on function overloading or if the function can simply pick one argument at a time using a dumb expression marker:
template<typename... Args>inline void pass(Args&&...){}
Which can be used as follows
template<typename... Args>inline void expand(Args&&... args) {
pass(some_function(args)...);
}
int main(int argc, const char * argv[])
{
expand<int, const char*, bool>(20, "Hello", true);
return 0;
}
Which will expand to something like this
pass(some_function(arg1), some_function(arg2), some_function(arg3) etc...);
The use of this pass function is necessary because the argument packs expands with separating by comma, but it can only be a coma of separating the function call arguments, not an "operator" function. Because of that "some_function(args)...;" will never work. Moreover, this above solution will only work when some_function return type is not void and it will do all the some_function calls in an unspecified order, because function argument evaluation order is not sequenced specifically. To avoid the unspecified order, brace enclosed initialiser list can be used, which guarantee strict left to right order evaluation. To avoid the need for a void return type, the comma operator can be used to always yield 1 in the expansion element.
struct pass {
template<typename... T> pass(T...){}
};
pass{(some_function(args), 1)...};
Instead of executing a function a lambda can also be specified and it will be executed in place, this allows executing arbitrary sequences of elements in place
pass{([&]{std::cout << args << std::endl;}(), 1)...};
pass{(std::cout << args << std::endl, 1)...};
int foo(){} // Termonation version or base case
template<typename Arg1, typename... Args>
int foo(const Arg1& arg1, const Args&... args)
{
process(arg1);
foo(args...); // Note arg1 does not appear here
}
If args... contains one or more arguments second version will be used and if it parameter pack is empty termination version will be used, which will eventually do nothing.Variadic templates can also be used in exception specification, base class list and constructor's initialisation list. For example
template<typename... Base> class Child : public Base... {
public:
Child(Base&&... base)
: Base(base)...
{
}
};
The unpack parameter will replicate the types for the base classes of Child such that this class will derived from each of the types passed in. Also, constructor must take a reference to each base class, so as to initialise bases classes of Child.In function templates, variadic parameters can be forwarded, When combined with rvalue references, this allows for perfect forwarding
template<typename T> struct SharedPtrAllocator {
template<typename... Args> static std::shared_ptr<T> constructWithSharedPtr(Args&&... args) {
return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
};
This unpacks the argument list into the constructor of T. The std::forward<Args>(args) syntax is the syntax that perfectly forwards arguments as their proper types, even with regard to rvalue-ness to the constructor. The unpack operator will propagate the forwarding syntax to each argument. This particular factory method automatically wraps the allocate memory in std::shared_ptr in order to avoid any memory leaks.The number of arguments in a parameter pack can be determined as
template<typename... Args> struct S {
static const int size = sizeof...(Args);
};
The syntax S<int, short>::size will be two while S<>::size will be zero.
- std::function or function object
function objects are semantically similar in nature to function pointers and have similar syntax as well but they are loosely bound as compared to function pointers and can be made referred to any thing like member functions, function pointers or simple functions. std::function object is defined in <functional>
#include <functional>
int sum(int a, int b)
{
return a + b;
}
int main(int argc, const char * argv[])
{
std::function<int(int, int)> add;
add = ∑
add(5, 6);
return 0;
}
Another example
Lets reconsider our previous example defined above in std::function section.
Logically std::bind can be declared as
The std::bind template generates a forwarding call wrapper for f. Calling this wrapper is equivalent of invoking f with its arguments bound to args.
Each argument can be either bound to a value or can be a placeholder(sometimes also called currying). The above sample is an example of bound to value, now lets see example to use placeholder arguments.
#include <functional>
int main(int argc, const char * argv[])
{
std::plus<int> plus;
std::function<int(int, int)> add2;
add2 = plus;
return 0;
}
std::plus<int> is declared inside <functional> and it can be thought of logically defined as
template<typename T>
T plus(const T& t1, const T& t2)
{
return t1 + t2;
}
- std::bind
Lets reconsider our previous example defined above in std::function section.
#include <functional>
int sum(int a, int b)
{
return a + b;
}
int main(int argc, const char * argv[])
{
auto plus = std::bind(sum, 5, 6);
int x = plus();
return 0;
}
Please note the use of "auto" here.
template<class F, class... Args>
/* unspecified */ bind (F&& f, Args&&... args);
template <class R, class F, class... Args>
/* unspecified */ bind (F&& f, Args&&... args);
Each argument can be either bound to a value or can be a placeholder(sometimes also called currying). The above sample is an example of bound to value, now lets see example to use placeholder arguments.
#include <functional>
float divide(float a, float b)
{
return a / b;
}
int main(int argc, const char * argv[])
{
auto div1 = std::bind(divide, 12, 6);
std::cout << div1() << std::endl;
auto div2 = std::bind(divide, std::placeholders::_1, std::placeholders::_2);
std::cout << div2(12, 3) << std::endl;
auto div3 = std::bind(divide, std::placeholders::_2, std::placeholders::_1);
std::cout << div3(4, 12) << std::endl;
return 0;
}
The Output is
2
4
3
The _1 placeholder tells where the first argument of result of std::bind(or in this particular example of div1, div2 or div3) is to go when "divide" is called through any one of these. The first argument is called _1, the second is _2 and so on. The placeholders are defined in std::placeholders namespace.
A member function can be treated as a simple function with an extra argument.
A member function can be treated as a simple function with an extra argument.
struct A {
int foo(int i)
{
return i + 5;
}
};
int main(int argc, const char * argv[])
{
std::function<int(A*, int)> f;
f = &A::foo; // Its now a pointer to member function
A a;
int r = f(&a, 3); // calls A::foo() for 'a' with 3
std::function<int (int)> ff = std::bind(f, &a, std::placeholders::_1);
r = ff(7); // calls a.foo(7)
return 0;
}
- std::tuple and std::tie
The tuple is N order collection of unrelated data types, where N range from 0 to large implementation defined value. Tuple can be though of an unnamed struct with members of specified tuple element types. tuple is defined in <tuple>
| C++11 | Python |
std::tuple<int, std::string, double> t(34, "text", 2.2);
int i = std::get<0>(t); // i = 34
std::string s = std::get<1>(t); // s = "text"
double d = std::get<2>(t); // d = 2.2
|
t = (34, 'text', 2.2) i = t[0] s = t[1] d = t[2] |
The elements of tuple can be explicitly specified or deuced by using make_tuple() and elements can be accessed by 0 based index using get()
auto t = std::make_tuple(std::string("hello"), 21, 56.11); // t is of type std::tuple<std::string, int, double>
std::string s = std::get<0>(t); // s = "hello"
int i = std::get<1>(t); // i = 21
double d = std::get<2>(t); // d = 56.11
The unpacking of a tuple into variables can be done using std::tie
int main(int argc, const char * argv[])
{
std::tuple<int, std::string, double> t = std::make_tuple(5, "word", 4.5);
int i = 0;
std::string string = "";
double d = 0.0;
std::tie(i, string, d) = t;
std::cout << "i = " << i << ", string = " << string << ", d = " << d << std::endl;
return 0;
}
The output is
i = 5, string = word, d = 4.5
std::tuple and std::tie and be used for lexicographical comparison as well.
| C++03 | C++11 |
struct Product {
std::string name;
float price;
int quantity;
bool operator < (const Product& o)
{
if (name < o.name)
return true;
if (name == o.name) {
if (price < o.price)
return true;
if (price == o.price)
return quantity < o.quantity;
}
return false;
}
};
std::set<Product> products;
|
struct Product {
std::string name;
float price;
int quantity;
bool operator < (const Product& o)
{
return std::tie(name, price, quantity) < std::tie(o.name, o.price, o.quantity);
}
};
std::set<Product> products;
|
- Uniform initialisation and std::initilizer_list
struct S {
int i;
std::string s;
};
S s = {1, "Text"}; // s.i = 1 and s.s = "Text"
S array[] = {{1, "First"}, {2, "Second"}};
C++03 only allows initializer list on structs or classes that conform of the definition of POD(Plain Old Data). C++11 extends initilizer-list so now they can be used on all classes like standard containers like std::vector etc.Initilizer list is not an array anymore. C++11 has introduced a new template std::initializer_list. This allows constructors and functions to take initializer-list as parameter.
And a function that take an initilizer-list as parameter looks like
void f(std::initializer_list<float> list)
{
// f implementation
}
int main(int argc, const char * argv[])
{
f({2.0, 2.5, 3.0, 3.5});
return 0;
A possible implementation of a container that take initilizer-list as parameter in its constructor might look like this.
template<typename T>
class Vector {
public:
Vector(std::initializer_list<T> list)
{
for (auto it = list.begin(); it != list.end(); ++it)
push_back(*it);
}
// other implementaion
};
The constructor is a special kind of constructor called an initilizer-list constructor. Initilizer-list constructor takes precedence over other constructor if present. At sometimes a few people might confuse and intermix the initilizer-constructor with other constructor. See the example below
std::vector<int> v1(5);// constructs a vector with five elements, i.e {0, 0, 0, 0, 0}
std::vector<int> v2{5};// constructs a vector with one element, i.e {5}
Also
struct S {
S(int, int);
S(std::initializer_list<int>);
};
S s1(5, 6); // calls first constructor
S s2{5, 6}; // calls initilizer_list constructor
- Uniform initilization
struct A {
int x;
double y;
};
struct B {
B(int x, double y) : _x(x) , _y(y){}
private:
int _x;
double _y;
};
A a{1, 2.5};
B b{1, 2.5};
The initialization of 'a' behaves exactly as though it were aggregate-initilizer. The initilization of 'b' invokes its constructor.It may be noted that during initilizaiton implicit conversion can be used is applicable, or if no conversion exists or if narrowing conversion exists.
struct Person {
std::string name;
int age;
};
Person person()
{
return {"MyName", 10};
}
And
int x = 2.5; // warning
int y{2.5}; // error: narrowing
std::vector<int> v = {2, 2.5, 45}; // eroor: narrowing
Also "The most vexing parse" problem.Do not mix std::initialiser_list with auto
int n = 0;
auto w(n); // int
auto x = n; // int
auto y{n}; // std::initilizer_list<int>
auto z = {n}; // std::initilizer_list<int>
- Suffix return type syntax
template <typename T, typename U>
??? add(T t, U u)
{
return t + u;
}
What we can write for return type? It is of course the "type of t + u". The first thought comes to mind is to use decltype
template <typename T, typename U>
decltype(t + u) add(T t, U u) // scope problem
{
return t + u;
}
This will not work because t and u are not in scope. However we can write
template <typename T, typename U>
decltype(*(T*)(0) + *(U*)(0)) add(T t, U u)
{
return t + u;
}
But this is ugly and error prone.The solution is to put the return type after the arguments
template <typename T, typename U>
auto add(T t, U u) -> decltype(t + u)
{
return t + u;
}
The auto means "return type is specified are deduced later".
The suffix is not really about templates and type deduction but it has to do more with scope- Explicit conversion operators
struct A {
A(int)
{
}
};
void f(A);
int main(int argc, const char * argv[])
{
A a(5);
A a2 = 5; // Silent implicit cast
A a2 = 5; // Silent implicit cast
f(5); // Silent implicit cast
return 0;
}
However if we use explicit constructor
struct A {
explicit A(int)
{
}
};
void f(A);
int main(int argc, const char * argv[])
{
A a(5);
A a2 = 5; // Error! implicit cast
A a2 = 5; // Error! implicit cast
f(5); // Error! implicit cast
return 0;
}
A constructor is not only method for defining a conversion. If we cannot modify a class we define a conversion operator from a different class.
struct A {
A(int)
{
}
};
struct B {
int m;
B(int x)
: m(x)
{
}
operator A()
{
return A(m);
}
};
void f(A);
int main(int argc, const char * argv[])
{
B b(5);
A a = b; // Silent, implcit constructor call
A a2(b); // Silent, implcit constructor call
f(b); // Silent, implcit constructor call
return 0;
}
But in C++11 we can have an explicit conversion operator as well, so the above example will look like
struct A {
A(int)
{
}
};
struct B {
int m;
B(int x)
: m(x)
{
}
explicit operator A()
{
return A(m);
}
};
void f(A);
int main(int argc, const char * argv[])
{
B b(5);
A a = b; // Error, explicit constructor call
A a2(b); // Ok, explicit constructor call
f(b); // Error, explicit constructor call
return 0;
}
- default and delete
struct A {
private:
A(const A&);
A& operator=(const A&);
};However in C++11 using delete we can achieve similar behavior
struct A {
A(const A&) = delete;A& operator=(const A&) = delete;
};
We can use default in a similar fashion
struct A {
int m;
A() = default;
A(int i)
: m(i)
{
}
};
In above example we are providing an overloaded constructor that takes an int as parameter, and we specify that we will be using default constructor.Similarly we can even further control what type arguments can be used. For example
struct A {
A(float); // Can initilize with float
A(long) = delete; // But not with long
virtual ~A() = default; // Using default destructor
};- Enum class, scoped and strongly typed enums
- The underlying type of an enum can not be specified, it is up to compiler whatever data type it chooses. This introduces compatibility issue(due to compiler differences) and enum cannot be forward declared.
- Enums export their enumerators to the surrounding scope which may cause name conflicts.
- Enums are not type safe, they can be implicitly converted to int which causes errors when someone does not want an enum to act as an int.
- constexpr - Generalised and guaranteed constant expression
int size()
{
return 5;
}
int arr[size()]; // Error, "Variable length array declaration not allowed"
This is illegal in C++03 because size() is not a constant expression. A C++03 compiler has no way of knowing that size() is actually a constant at runtime.C++11 has introduced another keyword constexpr that guarantees that a code block is constant at compile time. The above example can be written in C++11 as
constexpr int size()
{
return 5;
}
int arr[size()];
It will allow the compiler to understand and verify that size() is a compile time constant.Another example of constexpr
However using constexpr introduces some limitations on functions what it can do, these are
- Function must have a non void return type.
- Function body cannot declare variables or define new types.
- Function body may contain only declarations, null statements and single return statement.
- There must exist argument values such that after argument substitution the return steamiest must produce a constant expression.
Any member functions of classes such as copy constructor or other operators overloaded functions can be made constexpr provided that they meet the requirement of constexpr. This will allow compiler to copy classes at compile time and perform operations on them.
If a constexpr function is called with arguments which are not constexpr the function behaves as of it is a non-constexpr function and similarly in a constexpr function if its return statement does not evaluate to a constexpr for a particular case then it is not a constexpr function.
struct Point {
int x;
int y;
constexpr Point(int _x, int _y)
: x(_x)
, y(_y)
{
}
};
constexpr Point origin(0, 0);
Please note that constexpr is not general purpose replacement for const and vice versa.- Raw string literals
How do we put quotes in raw string, its easy
std::string s = R"("A quoted string")";
std::cout << s << std::endl;
Output
"A quoted string"
std::string s = R"###("A quoted string that contains terminator (")")###";
std::cout << s << std::endl;
Output
"A quoted string that contains terminator (")"
The character sequence before ( must be identical to after ). In the above example ### can be replaced with any sting and it must be maximum of 16 characters in length.
C++11 introduced two new character types char16_t and char32_t. There are three unicode encodings that C++11 supports, these are UTF-8, UTF-16 and UTF-32.
u8R"(This is a UTF-8 string)";
uR"(This is a UTF-16 string)";
UR"(This is a UTF-32 string)";
The first one has usual data type of const char[], second one has const char16_t[] and third one has const char32_t[].
C++11 also allows us to put unicode code prints directly into the string.
u8R"(This is a Unicode Character: \u2018.)"
uR"(This is a bigger Unicode Character: \u2018.)"
UR"(This is a Unicode Character: \U00002018.)"
- User defined literals
256 // int
3.5 // double
5.6f // float
'x' // char
2ULL // unsigned long long
0xAA // hexadecimal unsigned
"rb" // string
C++11 gives us the ability to define user defined literals. User defined objects are constructed through the use of string of characters.
Literal conversion takes place in two distinct stages, raw and cooked. Raw literal is sequence of characters or some specific types(not necessarily of char*) while cooked literal is of some other type. There is an exception to this rule and that is of string literals. In the above example "1234.555,34562.3" is in raw form(as a sequence of const char*).
A C++ literal 876 is raw literal, this is the sequence of characters '8', '7', '6' and in cooked literal it is integer 876. The C++ literal 0xFF is raw '0', 'x', 'F', 'F' while in cooked for it is integer 255.
All user defined literals use suffix(it is not possible to define through prefix). All user defined suffixes must start with underscore(_) and using only underscore(_) is reserved by standard.
Another way of processing integer and floating point is through the use of variadic templates.
Literal processing function can be overloaded like other functions.
Please note the use of namespace in order to avoid any namespace conflict that may arise when using may user defined literals in a global namespace. An example for usage of user defined literal can be found here.
2ULL // unsigned long long
0xAA // hexadecimal unsigned
"rb" // string
C++11 gives us the ability to define user defined literals. User defined objects are constructed through the use of string of characters.
namespace E {
struct Point {
double x;
double y;
Point(double _x, double _y)
: x(_x)
, y(_y)
{
}
};
namespace Literals {
Point operator "" _p(const char* data, size_t length)
{
std::string string(data);
size_t position = string.find(",");
return Point(std::stod(string.substr(0, position)), std::stod(string.substr(position + 1)));
}
}
}
using namespace E::Literals;
int main(int argc, const char * argv[])
{
E::Point point = "1234.555,34562.3"_p;
return 0;
}Literal conversion takes place in two distinct stages, raw and cooked. Raw literal is sequence of characters or some specific types(not necessarily of char*) while cooked literal is of some other type. There is an exception to this rule and that is of string literals. In the above example "1234.555,34562.3" is in raw form(as a sequence of const char*).
A C++ literal 876 is raw literal, this is the sequence of characters '8', '7', '6' and in cooked literal it is integer 876. The C++ literal 0xFF is raw '0', 'x', 'F', 'F' while in cooked for it is integer 255.
All user defined literals use suffix(it is not possible to define through prefix). All user defined suffixes must start with underscore(_) and using only underscore(_) is reserved by standard.
Another way of processing integer and floating point is through the use of variadic templates.
struct A {
// Implementaion
};
namespace Literals {
template<char...> A operator "" _a();
}
using namespace Literals;
int main(int argc, const char * argv[])
{
A a1 = 12345_a;
A a2 = 5.67_a;
return 0;
}
The instantiation of a1 takes place as operator "" _a<'1', '2', '3', '4', '5'>(). Please note that there is no terminating character in this form. This main reason of this is to make use of C++11 keyword constexpr which makes compiler to construct and transform literal completely at compile time provided that the object which is being constructed is constexpr constructable and copyable and also literal processing function is constexpr.Literal processing function can be overloaded like other functions.
struct A {
// Implementaion
};
namespace Literals {
A operator "" _a(const char*, size_t);
A operator "" _a(const wchar_t*, size_t);
A operator "" _a(const char16_t*, size_t);
A operator "" _a(const char32_t*, size_t);
}
using namespace Literals;
int main(int argc, const char * argv[])
{
A a1 = "12345"_a; // Uses const char* overloaded version
A a2 = u8"12345"_a; // Uses const char* overloaded version
A a3 = L"12345"_a; // Uses const wchar* overloaded version
A a4 = u"12345"_a; // Uses const char16_t* overloaded version
A a5 = U"12345"_a; // Uses const char32_t* overloaded version
return 0;
}
- Range based for loops
int main(int argc, const char * argv[])
{
int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9,};
std::list<int> list;
for (auto x : array) {
std::cout << x << std::endl;
list.push_back(x);
}
for (int& i : list) // accessing each member by reference so that we are able to midify values
++i;
std::cout << "After " << std::endl;
for (auto it : list)
std::cout << it << std::endl;
return 0;
}
The output is
1
2
3
4
5
6
7
8
9
After
2
3
4
5
6
7
8
9
10
- Right angle brackets
std::list<std::vector<std::string>> listOfVectors;
A C++03 compiler will give an error on the above statement. If we put a space between last to right angle brackets the error will go away(however some programmers may forget to put that space). The question is why is this even a problem, we will have to look into the compilation stages in a compiler. A compiler front end consists of following stages- Lexical analyser, it makes up tokens from characters.
- Syntax analyser, it performs the grammar checking.
- Type checking, it finds the type of names and expressions.
Theoretically these stages are independent of each other, so the lexical analyzer has no way of knowing the >> is really a right shift operator or is part of a nested template instantiation statement.
C++11 dictates an improved specification of parser, so that multiple right angle brackets are interpreted as closing template arguments where necessary. The intended behaviour can further be explicitly specified by the use of parenthesis around the parameter expressions which involve >, >=, or >> binary operators.
template<bool B> struct A;
std::vector<A<1>2>> a1;
// Interpreted as std::vector of A<true> followed by "2 >> a1"
// , which is not a legal expression. 1 is true
std::vector<A<(1>2)>> a2;
// Interpreted as std::vector of A<fasle> followed by declaration of a2
//, which is legal syntax. (1>2) is false
- Control and query object alignment
alignof operator takes a type and returns a power of 2 byte boundary on which the instances of type must be allocated(in std::size_t). When given referenced type alignof gives the referenced type's alignment and when given array type it returns the individual element type's alignment.
constexpr size_t size = alignof(long);
size contains the alignment of type long.
alignas controls the memory alignment for a variable. It takes a constant or a type.
alignas(double) char array[5 * sizeof(double)];
In above expression we have requested an array to hold five doubles which is suitable for holding doubles.- Wrapper references
void increment(int& i)
{
++i;
}
template<typename T, typename U>
void foo(T t, U u)
{
t(u);
}
int main(int argc, const char * argv[])
{
int i = 3;
foo(increment, i);
// foo is instantiated as foo<void (int&), int>, i is not incremented
std::cout << "i = " << i << std::endl;
foo(increment, std::ref(i));
// foo is instantiated as foo<void (int&), std::reference_wrapper<int> >, i is incremented
std::cout << "i = " << i << std::endl;
return 0;
}
It is added in <utility>.
- std::unique_ptr
- It has memory ownership of object that it holds a pointer to
- It is not copy-constructable or copy-assignable however it is move-constructable and move-assignable.
- It deletes the object it has pointer to when itself goes out of scope.
Please note that unique_ptr does what previously auto_ptr does in C++98, however auto_ptr is depreciated now in C++11.
#include <memory>
class A {
public:
static std::unique_ptr<A> create()
{
std::unique_ptr<A> localPtr(new A()); // localPtr has object ownership
return std::move(localPtr);// transfering(moving) ownership out of localPtr
}// localPtr destructor called, its safe becaue we have already
// transfered(moved) ownership, localPtr internal pointer is null(or more
// correctly nullptr)
private:
A()
{
}
// Rest of implementation
};
int main(int argc, const char * argv[])
{
std::unique_ptr<A> objPtr = A::create(); // objPtr has sole ownership of object
return 0;
} // objPtr destructor is called, objPtr will delete its internal pointer
- std::shared_ptr
#include <memory>
class A {
public:
static std::shared_ptr<A> create()
{
return std::shared_ptr<A>(new A()); // refCount = 1
} // refCount = 2, because the return type is being creted
// refCount = 1, because the anonymous first shared_ptr destructor is being called
private:
A()
{
}
// Rest of implementation
};
int main(int argc, const char * argv[])
{
std::shared_ptr<A> objPtr = A::create(); // refCount = 2, objPtr is created, then refCount = 1 because the return value is being destroyed
{
std::shared_ptr<A> anotherPtr = objPtr; // refCount = 2
{
std::shared_ptr<A> yetAnotherPtr(anotherPtr); // refCount = 3
// other stuff ......
} // refCount = 2, yetAnotherPtr destructor is being called here;
} // refCount = 1, anotherPtr destructor is being called here
return 0;
} // weakCount = 0, weakPtr1 destructor is called here and object being
// pointed is being deleted here
- std::weak_ptr
#include <memory>
class A {
public:
static std::shared_ptr<A> create()
{
return std::shared_ptr<A>(new A()); // refCount = 1
} // refCount = 2, because the return type is being creted
// refCount = 1, because the anonymous first shared_ptr destructor is being called
private:
A()
{
}
// Rest of implementation
};
int main(int argc, const char * argv[])
{
std::shared_ptr<A> objPtr = A::create(); // refCount = 2, , weakCount = 0, objPtr is created, then refCount = 1 because the return value is being destroyed
std::weak_ptr<A> weakPtr1 = objPtr; // refCount = 2, weakCount = 1
{
std::shared_ptr<A> anotherPtr = objPtr; // refCount = 2, weakCount = 1
{
std::shared_ptr<A> yetAnotherPtr(anotherPtr); // refCount = 3, weakCount = 1
std::weak_ptr<A> weakPtr2(anotherPtr); // refCount = 3, weakCount = 2
// other stuff ......
} // refCount = 2, yetAnotherPtr destructor is being called here; weakCount = 1 because weakPtr2 destructor is called
} // refCount = 1, weakCount = 1, anotherPtr destructor is being called here
return 0;
} // weakCount = 0, weakPtr1 destructor is called here
// refCount = 0, objPtr destructor is being called here and finally internal
// object being pointed is being deleted here
- std::string
- std::vector
- STL
- std::thread
Threads in C++11 are constructed using std::thread class which can be passed a function, function pointer etc or even a lambda.
void foo(const std::list<int>&);
struct Foo {
std::list<int> list;
Foo(const std::list<int>& l)
: list(l)
{
}
void operator()();
};
int main(int argc, const char * argv[])
{
std::list<int> l1;
std::list<int> l2;
std::thread t1{std::bind(foo, l1)};// foo(l1) will run in a seprate thread
std::thread t2{Foo(l2)}; // Foo(l2)() will run in a seprate thread
std::thread t3([](){}); // lambda will run in a seprate thread
t1.join(); // wait for t1 to complete
t2.join(); // wait for t2 to complete
t3.join(); // wait for t3 to complete
return 0;
}
Calling join method on a thread gives us guarantee that the process does not terminates until the thread finishes execution. std::thread class has a method native_handle() that will return std::native_handle_type which maps to the native implementation defined thread handler. On a POSIX machine std::native_handle_type will map to pthread_t.
- std::mutex
#include <mutex>
static int shared = 0;
static std::mutex mutex;
void foo()
{
mutex.lock();
++shared;
mutex.unlock();
}
The code between lock() and unlock() is called critical region. It is general rule that critical region should be short enough. Only one of the thread at a time can operate in critical region, if another thread tries to enter into critical region it must first acquire lock on mutex, and second thread is blocked after call to lock() until fist thread leaves its critical region and calls union() on mutex.There are other problems that may arise when programming in a multi-threaded environment. I will talk on the problems and how language support to avoid these problems some time later.
- std::guard_lock and std::mutex
#include <mutex>
static int shared = 0;
static std::mutex mutex;
void foo()
{
std::lock_guard<std::mutex> mutexLocker(mutex);
++shared;
}
- std::async
- std::future and std::promise
For C++11 features support for Clang(click here) , gcc(click here) and Microsoft Visual studio(click here).







No comments:
Post a Comment