Introduction
C++0x is now a formal standard and will hereafter be called as C++11. It was ratified by ISO C++ in the year 2011.
The purpose of this article is to give a bird's eye of most of the C++11 features and for those features which are already shipped into VS2010, a deep analysis is given. This article can serve as a platform to start comprehensive study on individual features.
The article is divided into parts so that the length is at bay. I myself get scared to read lengthy stuff. Moreover, it will be boring to grab everything in one sitting.
As this is my first article on CodeProject, please bear with the formatting and with the typos, if any.
Background
It took almost a century for Charles Babbage's Difference Engine to evolve into a electrically powered computer. In the 1940s, only assembly level languages were used owing to the then computer's low speed and memory capacity. Things started to turn after a decade and the period between 1950 to 1970 saw blooming of many programming languages of which many have survived till date.
In 1979, Bjarne Stroustrup working for the Bell labs began enhancing the "C" language first adding classes, then virtual functions, operator overloading, multiple inheritance, templates, and exception handling among other features. He called it initially "C with Classes". It was renamed to C++ [++ may be to say that it increments C] in the year 1983.
C++ Landmarks/Iterations Time Line
- 1983 - First commercial C++ compiler
- 1998 - C++ standards committee standardized C++ [C++98]
- 2003 - A bug patch with no new features [C++03]
- 2005 - A technical report called “Library Technical Report” (TR1 for short) was released
- 2011 - Introduced significant number of features and also enhanced C++ standard library.
As we can see, this iteration is the biggest one (ok..ok...STL addition may also be big).
Do We Have to Know this New Standard ??
YES! The sooner the better. Resisting change is human. But we programmers/developers will be out of a job the day when every language or project reaches static phase. We like to be in dynamic projects and the same is with language.
Change is inevitable and when an expert committee brainstormed for almost a decade, then the result will obviously be beautiful and fruitful.
Even if we are not interested in incorporating these new iterations in the code, a quick glance at the features will help us to avoid or even think before coding certain scenarios using old compilers. Moreover, by just switching to a compiler supporting C++11, we will be immensely benefited as the Standard template library is enhanced and revamped for performance. So if your project is using STL contianers/algorithms, then switch as early as possible.
C++11 Features
Here is a table summarizing core C++11 features and also their implementation status in VS2010.
Feature | Intent | VS2010 status |
| Usability improvement | yes |
| Usability improvement | yes |
| Usability & Performance improvement | yes |
| Usability improvement | yes |
| Usability improvement | yes |
| Usability & Performance improvement | yes |
| Type safety improvement | partial |
| Performance improvement | yes |
| Performance improvement | yes |
| Performance improvement | yes |
| Usability improvement | yes |
| Usability & Performance improvement | yes |
| Performance improvement | no |
| Usability & Performance improvement | no |
STL Enhancements |
| | yes |
| yes |
| yes |
| yes |
| yes |
| yes |
...... and many more |
Individual Features
This feature is introduced in an effort to make C++ a more usable language. The committee has given a new meaning for the 'auto
' keyword [just to remind readers that the old auto
keyword is used to declare a variable having local scope and all local variables if their storage class is not specified as static, extern or register are implicitly converted to auto
storage-class specifier].
As per the new interpretation, auto
helps in deducing the type of the defined object by inspecting the RHS expression or its initializer.
auto i = 5
int n=3;
double pi=3.14;
auto j=pi*n;
Now, let's take a case where the type is hard to write:
map< int, map<int,int> > _Map;
map<int, map<int,int>>::const_iterator itr1 = _Map.begin();
const auto itr2 = _Map.begin();
Now, take a case where the type is hard to know:
template<class U, class V>
void Somefunction(U u, V v)
{
??? result = u*v;
auto result = u*v;
}
I will be extending this function for the next feature to know more usability of auto
. The auto
keyword finds more usage while declaring and initializing a variable to a lambda expression [we will cover lambdas soon].
Few points on this feature:
- We can use
const
, volatile, pointer(*), reference (&), rvalue
reference (&& - we will know about this shortly) specifiers on auto
keyword:
auto k = 5;
auto* pK = new auto(k);
auto** ppK = new auto(&k);
const auto n = 6;
- A variable declared as
auto
must have an initializer:
auto m;
- An
auto
keyword cannot be joined with another type:
auto int p;
- A method/template parameters cannot be declared as auto:
void MyFunction(auto parameter){}
template<auto T> void Fun(T t){}
- A variable that is declared on heap with the
auto
keyword using expression must have an initializer:
int* p = new auto(0); int* pp = new auto();
auto x = new auto();
auto* y = new auto(9); auto z = new auto(9);
- As
auto
keyword is a placeholder for a type, but is not a type itself, hence auto
cannot be used for typecasting or operators such as sizeof
and typeid
.
int value = 123;
auto x2 = (auto)value;
auto x3 = static_cast<auto>(value);
- All the variables in a declarator list that is declared with the
auto
keyword must resolve to the same type:
auto x1 = 5, x2 = 5.0, x3='r';
- Auto does not deduce CV-qualifiers (constant & Volatile qualifiers) unless declared as a reference:
const int i = 99;
auto j = i; j = 100
auto& k = i; k = 100;
auto
decays arrays to pointers unless declared as a reference:
int a[9];
auto j = a;
cout<<typeid(j).name()<<endl;
auto& k = a;
cout<<typeid(k).name()<<endl;
return_value decltype (expression)
[return_value is the type of the expression parameter]
This can be used to determine the type of a expression. As hinted by Bjarne, if we just need the type for a variable that we are about to initialize, auto
is often a simpler choice. But if we need a type for something that is not a variable, such as a return type, then decltype
is the thing we should be trying.
Now let's look back at an example we worked earlier:
template<class U, class V>
void Somefunction(U u, V v)
{
result = u*v;
decltype(u*v) result = u*v; }
In the next section, I will make you familiar with the notion of combining auto and decltype
to declare template functions whose return value type depends on its template arguments.
Few points on decltype
:
- If the expression is a function, then
decltype
gives the type of the return of the function:
int add(int i, int j) { return i+j; }
decltype( add(5,6) ) var = 5;
- If the expression is an
lvalue
, then decltype
gives lvalue
reference to the type of the expression.
struct M { double x; };
double pi = 3.14;
const M* m = new M();
decltype( (m->x) ) piRef = pi;
- It is important to note that
decltype
does not evaluate the expression as auto
does but just deduces the type of the expression:
int foo(){}
decltype( foo() ) x;
This is completely a new feature for C++ developers. Till now, the type the return value of a function should go before the function's name. From C++11, we can also put the return type at the end of the function declaration, of course only after substituting auto for the name of the return type. Now why do we want to do this. Let's find out:
template<class U, class V>
??? Multiply(U u, V v) {
return u*v;
}
We cannot obviously do like:
template<class U, class V>
decltype(u*v) Multiply(U u, V v) {
return u*v;
}
In this situation, we can use auto
and then latter once u
&v
's definitions are known, we can specify the return type using decltype
.
Cool, isn't it?
template<class U, class V>
auto Multiply(U u, V v) -> decltype(u*v) {
return u*v;
}
Look at this declaration:
map<int, vector<int>> _Map;
This is an error with earlier compilers as there is no space between >
s and the compiler will treat it as right shift operator.
But C++11 compilers will parse these multiple right angle brackets as closing to the template argument list and saves us from the need of putting space between >
.
This is not a great feature when compared to the rest, but as we C++ developers look for perfection, here is the one that is taken care of.
This macro can be used to detect and diagnose compile-time errors. Compile-time period. This is in contrast to the CRT-assert macro which is an assertion at run-time. This goody can be used to check program invariants at compile-time.
This takes an expression that can be evaluated to bool
and a string
literal. If the expression evaluates to false
, then the compiler issues an error containing the specified string
literal and the compilation is failed. If true
, the declaration has no effect.
We can use static_assert
at:
- namespace/global scope:
static_assert(sizeof(void *) == 4, "Oops...64-bit code generation is not supported.");
- class scope:
template<class T, int _n>
class MyVec
{
static_assert( _n > 0 , "How the hell the size of a vector be negative");
};
void main()
{
MyVec<int, -2> Vec_;
MyVec<int, 100> Vec_;
}
- block scope:
template<typename T, int div>
void Divide( )
{
static_assert(div!=0, "Bad arguments.....leading to division by zero");
}
void main()
{
Divide<int,0> ();
}
Do remember that since static_assert
is evaluated at compile time, it cannot be used to check assumptions that depend on run-time values like the arguments of a function:
void Divide(int a, int b)
{
static_assert(b==0, "Bad arguments.....leading to division by zero");
}
The static_assert
declaration is especially useful for debugging templates. The compiler evaluates the constant-expression parameter immediately if it does not depend on a template parameter. Otherwise, the compiler evaluates the constant-expression parameter when the template is instantiated.
This feature is introduced mainly to take care of the pitfalls generated by using the (infamous and nasty) NULL
macro. As we all know, NULL
is nothing but a preprocessor expanding to 0
at compile time and this expansion often leads to ambiguity. Take for instance:
void SomeFunction(int i){ }
void SomeFunction(char* ch) { }
Now a call like: SomeFunction(NULL)
will always be resolved to SomeFunction(int i)
, even though we want to call SomeFunction(char* ch)
with null
pointer argument.
To force, we have to call like: SomeFunction( (char*) NULL ) // yak ..ugly
To avoid these inconveniences, the nullptr
is introduced finally. The nullptr
literally means null
pointer is not an integer. This can thus be safely used to indicate that an object handle, interior pointer, or native pointer type does not point to an object.
Some of the features are covered in the second part of this series. Please refer to "C++11 – A Glance [part 2 of n]".
Rest of the features will be covered in the coming parts.
Thank you for reading this article. It would be helpful if you rate/send feedback, so that I can improve while working on the remaining parts or updating this part with new information.
Other Sources
As the standard has been frozen just 3 months ago, there are no books describing the new features. Here are few references which are useful to get a deep insight on all the features.
Acknowledgments
Thanks to Clement Emerson for his views and review.
History
- January 09, 2012: Added Introduction and Part-1
- January 14, 2012: Added links to Part-2, "C++11 – A Glance [part 2 of n]"
- January 15, 2012: A typo corrected
- January 21, 2012: Corrected few broken links [no additional information]
- January 25, 2012: Added few points to
auto
and decltype
(as mentioned by user 'ephisino') - February 03, 2012: Corrected few typos [no additional information]