Wednesday, October 9, 2013

Why we should care for C++11

Bjarne Stroustrup, the creator of C++, said recently that C++11 “feels like a new language — the pieces just fit together better.” Indeed, core C++11 has changed significantly. It now supports lambda expressions, automatic type deduction of objects, uniform initialization syntax, delegating constructors, deleted and defaulted function declarations, nullptr, and most importantly, rvalue references — a feature that augurs a paradigm shift in how one conceives and handles objects. And that’s just a sample.
The C++11 Standard Library was also revamped with new algorithms, new container classes, atomic operations, type traits, regular expressions, new smart pointers, async() facility, and of course a multithreading library.
biggest changes in the language, and why they are such a big deal. As you’ll see, threading libraries are not the only change. The new standard builds on the decades of expertise and makes C++ even more relevant. As Rogers Cadenhead points out, “That’s pretty amazing for something as old as disco, Pet Rocks, and Olympic swimmers with chest hair.”
First, let’s look at some of the prominent C++11 core-language features.

Lambda Expressions
A lambda expression lets you define functions locally, at the place of the call, thereby eliminating much of the tedium and security risks that function objects incur. A lambda expression has the form:
[capture](parameters)->return-type {body}
The [] construct inside a function call’s argument list indicates the beginning of a lambda expression. Let’s see a lambda example.
Suppose you want to count how many uppercase letters a string contains. Using for_each() to traverses a char array, the following lambda expression determines whether each letter is in uppercase. For every uppercase letter it finds, the lambda expression increments Uppercase, a variable defined outside the lambda expression:
int main()
{
   char s[]="Hello World!";
   int Uppercase = 0; //modified by the lambda
   for_each(s, s+sizeof(s), [&Uppercase] (char c) {
    if (isupper(c))
     Uppercase++;
    });
 cout<< Uppercase<<" uppercase letters in: "<< s<<endl;
}
It’s as if you defined a function whose body is placed inside another function call. The ampersand in [&Uppercase] means that the lambda body gets a reference to Uppercase so it can modify it. Without the ampersand, Uppercase would be passed by value. C++11 lambdas include constructs for member functions as well.

Automatic Type Deduction and decltype
In C++03, you must specify the type of an object when you declare it. Yet in many cases, an object’s declaration includes an initializer. C++11 takes advantage of this, letting you declare objects without specifying their types:
auto x=0; //x has type int because 0 is int
auto c='a'; //char
auto d=0.5; //double
auto national_debt=14400000000000LL;//long long
Automatic type deduction is chiefly useful when the type of the object is verbose or when it’s automatically generated (in templates). Consider:
void func(const vector<int> &vi)
{
vector<int>::const_iterator ci=vi.begin();
}
Instead, you can declare the iterator like this:
auto ci=vi.begin();
The keyword auto isn’t new; it actually dates back the pre-ANSI C era. However, C++11 has changed its meaning; auto no longer designates an object with automatic storage type. Rather, it declares an object whose type is deducible from its initializer. The old meaning of auto was removed from C++11 to avoid confusion.
C++11 offers a similar mechanism for capturing the type of an object or an expression. The new operator decltype takes an expression and “returns” its type:
const vector<int> vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;


Deleted and Defaulted Functions
A function in the form:
struct A
{
 A()=default; //C++11
 virtual ~A()=default; //C++11
};
is called a defaulted function. The =default; part instructs the compiler to generate the default implementation for the function. Defaulted functions have two advantages: They are more efficient than manual implementations, and they rid the programmer from the chore of defining those functions manually.
The opposite of a defaulted function is a deleted function:
int func()=delete;
Deleted functions are useful for preventing object copying, among the rest. Recall that C++ automatically declares a copy constructor and an assignment operator for classes. To disable copying, declare these two special member functions =delete:
struct NoCopy
{
 NoCopy & operator =( const NoCopy & ) = delete;
 NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
NoCopy b(a); //compilation error, copy ctor is deleted

nullptr

At last, C++ has a keyword that designates a null pointer constant. nullptr replaces the bug-prone NULL macro and the literal 0 that have been used as null pointer substitutes for many years. nullptr is strongly-typed:

void f(int); //#1
void f(char *);//#2
//C++03
f(0); //which f is called?
//C++11
f(nullptr) //unambiguous, calls #2
nullptr is applicable to all pointer categories, including function pointers and pointers to members:
const char *pc=str.c_str(); //data pointers
if (pc!=nullptr)
  cout<<pc<<endl;
int (A::*pmf)()=nullptr; //pointer to member function
void (*pmf)()=nullptr; //pointer to function

Delegating Constructors
In C++11 a constructor may call another constructor of the same class:
class M //C++11 delegating constructors
{
 int x, y;
 char *p;
public:
 M(int v) : x(v), y(0), p(new char [MAX]) {} //#1 target
 M(): M(0) {cout<<"delegating ctor"<<endl;} //#2 delegating
};

Rvalue References
Reference types in C++03 can only bind to lvalues. C++11 introduces a new category of reference types called rvalue references. Rvalue references can bind to rvalues, e.g. temporary objects and literals.
The primary reason for adding rvalue references is move semantics. Unlike traditional copying, moving means that a target object pilfers the resources of the source object, leaving the source in an “empty” state. In certain cases where making a copy of an object is both expensive and unnecessary, a move operation can be used instead. To appreciate the performance gains of move semantics, consider string swapping. A naive implementation would look like this:
void naiveswap(string &a, string & b)
{
 string temp = a;
 a=b;
 b=temp;
}
This is expensive. Copying a string entails the allocation of raw memory and copying the characters from the source to the target. In contrast, moving strings merely swaps two data members, without allocating memory, copying char arrays and deleting memory:
void moveswapstr(string& empty, string & filled)
{
//pseudo code, but you get the idea
 size_t sz=empty.size();
 const char *p= empty.data();
//move filled's resources to empty
 empty.setsize(filled.size());
 empty.setdata(filled.data());
//filled becomes empty
 filled.setsize(sz);
 filled.setdata(p);
}
If you’re implementing a class that supports moving, you can declare a move constructor and a move assignment operator like this:
class Movable
{
Movable (Movable&&); //move constructor
Movable&& operator=(Movable&&); //move assignment operator
};
The C++11 Standard Library uses move semantics extensively. Many algorithms and containers are now move-optimized.
C++11 Standard Library
C++ underwent a major facelift in 2003 in the form of the Library Technical Report 1 (TR1). TR1 included new container classes (unordered_set, unordered_map, unordered_multiset, and unordered_multimap) and several new libraries for regular expressions, tuples, function object wrapper and more. With the approval of C++11, TR1 is officially incorporated into standard C++ standard, along with new libraries that have been added since TR1. Here are some of the C++11 Standard Library features:

1 comment: