Ruminations on C++11

///////////////////////////////////////////////////////////////////////////

// Preface

//////////

//

// This is a blog about the new version of C++, C++11. It has been many years in the

// making and adds lots of new features to the C++ language. Some of those features

// are already supported by commonly used compilers, a description of these features

// follows.

//

// As you can see, this ‘blog’ is a bit unusual. It’s actually a C++ source code file

// annotated with lots of comments. So it can be read on its own, with the code being

// examples of the things being described, or it can be viewed in Visual Studio (VS)

// 2010, built and even executed. With a few minor adjustments pointed out along the

// way, it can also be built using GCC and executed on whatever system that it is

// installed on.

 

/////////////////////////////////////////////////////////////////////////////////////////

// Ignore these, I couldn’t think of anywhere else to put them.

#include “stdafx.h”

#include <vector>

#include <algorithm>

#include <map>

#include <iostream>

/////////////////////////////////////////////////////////////////////////////////////////

// Ruminations on C++11 (the bits currently supported anyway).

//////////////////////////////////////////////////////////////

//

// Of the many new features of C++11, only a subset are available on stable releases of

// the well-used contemporary C++ compilers. I thought that it would be a good exercise

// in understanding these new features to try them out. I decided that I’d try VS2010 on

// Windows 7 and GCC on Ubuntu, as these are easy for me to access and a reasonable

// approximation of other developers environments.

//

// The main differences being there is no support for nullptr in GCC, and no support for

// initialiser lists in VS2010. Of these, the initialiser lists seem the most useful feature, I hope it gets into VS soon.

//

// The features described here are:

//     * static_assert

//     * trailing return types

//     * constructors calling other constructors in the same class

//     * lambda functions

//     * initialiser lists

//     * a fix for the ‘std::map< int, std::vector<int>>’ ‘>>’ problem

//     * ‘auto’ variable declarations

//     * ‘decltype’ variable declarations

//     * rvalue references, move constructors and perfect forwarding

//     * nullptr, improved typing for the null pointer value

//

// I have annotated some C++11 code that shows the new features, this code does not fully

// comply with our coding standards in order for the descriptions to be clearer. This

// blog contains code that compiles with VS2010, the GCC supported C++11 features are

// provided in the comments.

// 1. static_assert

///////////////////

//

// First we have an example of ‘static_assert’, these are compile time assertions. In

// this case, a check is made to ensure that the size of the type used to instantiate the

// object is sufficient. This check occurs at compile time, so the supplied error message

// is displayed as a compile error if the condition is false.

//

template<class T>

class TemplateTest

{

// if the size of the type used to instantiate an object is too small, a compile

// time error is produced.

static_assert( sizeof(T) > 1, “sizeof(T) not > 1!” );

T m_test;

};

// 2. Trailing return type declarations

///////////////////////////////////////

//

// Next up, an example of trailing return types on method declarations.

// If we have a class that defines an enumeration (very handy for code clarity).

class MyEnumClass

{

public:

enum MyHelpfulEnum

{

An,

Enumeration,

That,

Usually,

Helps,

To,

Clarify,

Code

};

MyEnumClass();

MyEnumClass( MyHelpfulEnum initialValue );

// We can declare the method like this, with a trailing return type,

static auto KeepCodeClear() -> MyHelpfulEnum;

// or like this, it makes no difference to the compiler.

//static MyHelpfulEnum KeepCodeClear();

// But I think it is helpful to keep things consistent.

};

// The advantage of this feature comes when declaring the method implementation, as this

// method returns an enumeration defined within the class. Previously you’d have to

// declare the method decorated with lots of scope to help the compiler know what was

// going on (it has enough to contend with without having to deal with this kind of

// thing). Like so,

//

//MyEnumClass::MyHelpfulEnum MyEnumClass::KeepCodeClear()

//

// But now you can declare the method like this.

auto MyEnumClass::KeepCodeClear() -> MyHelpfulEnum

// The ‘->’ is a little different, but less repetition is involved (sort of).

{

// we’ll see more from ‘auto’ later (why keep a keyword for one use when it can

// have three!?)

return Usually;

}

// 3. One constructor calling another

/////////////////////////////////////

//

// Here we have an example of one constructor calling another, I found it annoying that

// this was missing from C++ in the past.

MyEnumClass::MyEnumClass()

{

// this constructor can call another constructor.

MyEnumClass( Helps );

}

MyEnumClass::MyEnumClass( MyHelpfulEnum initialValue )

{

}

// Here are some function prototypes for the nullptr description (something to look

// forward to).

void NullPointerCall( int notAPointerParameter );

void NullPointerCall( int* aPointerParameter );

// 4. Lambda functions

//////////////////////

//

int _tmain(int argc, _TCHAR* argv

 

[])

{

// Next we have an example of a lambda function (familiar to C# developers). I

// have to admit I’m not a fan of these; if it’s a function make it one, at least

// it can be called from other places then. Now I’ve got that rant out of the way

// we can continue!

//

// If we have a list of interest:

std::vector<int> anInterestingList;

// And that list has some elements, in GCC we can use initialiser lists, like

// so…

//std::vector<int> anInterestingList{1,2,3,4}; // so much nicer.

//

// But for VS we cannot, so add some elements the old fashioned way.

anInterestingList.push_back( 1 );

anInterestingList.push_back( 2 ); // boo

anInterestingList.push_back( 3 );

anInterestingList.push_back( 4 ); // hiss

// Declare an accumulator for our demonstration.

int accumulator( 0 );

// From time to time, it is necessary to loop through all the elements in a list

// (not all that often, but it does happen).

std::for_each( anInterestingList.begin(), anInterestingList.end(),

// Below is the lambda function, the start is defined by ‘[‘, the ampersand

// defines the capture list (the variables accessible by the lambda function),

// if the capture list is empty, i.e. there is only ‘&’, all variables in scope

// are available. It seems like good practice to explicitly state what is

// accessible rather than implying everything. If no ‘&’ appears, i.e ‘[]’ is

// used, no variables are captured. There is also, like any other function a

// parameter list.

[&accumulator](int element){ accumulator += element; } );

// the lambda function, accumulates the values in the list.

std::cout << “Accumulator = ” << accumulator << std::endl;

// 5. ‘>>’ in variable declarations

///////////////////////////////////

//

// The next feature is more of a fix than anything else. Allowing ‘>>’ in

// declarations. Of course you could always get around this problem by sub

// classing, (or even using typedef, but that’s not very OO is it?) but if you did

// do this the error message was, well, …confusing (I’ll leave it at that before

// I say something I shouldn’t).

std::map< int, std::vector<int>> lookItWorks;

// It’s better to subclass if you can though. This is not a C++11 feature, but I

// rarely see it in C++ and it seems so obviously OO.

class AListOfInts : public std::vector<int>{};

class AMapOfIntvsInts : public std::map< int, AListOfInts>{};

// Of course if you wish to use the constructors of the base classes this method

// is annoying. If only there was a way…

AMapOfIntvsInts anAsideToProceedings;

// Well C++11 allows the use of ‘using’ to use base class constructors in derived

// classes, which helps this ‘problem’. If they were implemented by VS or GCC

// I’d add an example.

// 6. ‘auto’ types for variable declarations

////////////////////////////////////////////

//

// This very simple extract of code shows another new use of the auto keyword.

// Here you let the compiler automatically deduce the type.

auto autotest( anInterestingList.begin() );

// 7. ‘decltype’ types for variable declarations

////////////////////////////////////////////////

//

// Similar to the ‘auto’ type deduction feature above, this time declaring a type

// that is the same as another type.

decltype(autotest) decltypetest(autotest);

// declare decltypetest as the same type as autotest.

std::cout << “*autotest = ” << *autotest << std::endl;

std::cout << “*decltypetest = ” << *decltypetest << std::endl;

// 8. rvalue references

///////////////////////

//

// As you can only assign lvalues (like variables, objects and other references)

// to references, C++11 introduces rvalue references for the others (like

// temporaries and literals). I’m not going to get into the details here as

// those subjects require whole documents to themselves. So you can have:

int aValue( 9 );

int& myPreferenceIsAReference( aValue );

int& alsoReferingToAValue( myPreferenceIsAReference );

// But you can’t have:

// MyEnumClass::MyHelpfulEnum& isTheCodeClear( MyEnumClass::KeepCodeClear() );

// as the return from MyEnumClass::KeepCodeClear() is a temporary variable.

//

// In C++11 using rvalue references you can:

MyEnumClass::MyHelpfulEnum&& isTheCodeClear( MyEnumClass::KeepCodeClear() );

if ( isTheCodeClear == MyEnumClass::Usually )

{

std::cout << “isTheCodeClear == MyEnumClass::Usually, must do better!”

<< std::endl;

}

// You can also do things that seem very strange.

int&& rValueRef = 2;

// assign the rvalue reference to a reference to an rvalue, in this case the

// literal number 2.

++rValueRef;

// Has this blown your mind? We have incremented 2! Surely all that we know and

// love is doomed. It’s ok though, as rValueRef is assigned a reference to a

// temporary variable, this is saving time rather than creating a temporary

// and then copying it to a variable.

std::cout << “rValueRef referencing the literal number 2 = “

<< rValueRef << ” OMG!” << std::endl;

// These two examples show that less copying of values is attained by using rvalue

// references. The C++11 standard libraries and STL have been written to take

// advantage of this to improve performance without requiring users of these

// libraries to do anything different (apparently). There’s lots of talk about

// ‘move semantics’ and ‘perfect forwarding’ which is nice if you like that kind

// of thing, these subjects require blogs of their own.

// In summary, they allow less copying, making copy constructors and equals

// operators more efficient. This in turn helps developers to write logical

// code that is far less likely to cause performance issues.

// 1. (again) static_assert

///////////////////////////

//

// static_assert (for compile time assertions). This time we are using the

// static_assert feature to check the sanity of a constant value against another

// constant. This is especially useful to stop people changing constants to

// erroneous values.

const int someConfigValue = 1;

const int someConfigValueCheck = 2;

// As before in the template example, if the condition is false the compiler

// outputs the error text supplied.

static_assert( someConfigValue < someConfigValueCheck,

“someConfigValue is not less than someConfigValueCheck!” );

TemplateTest<int> inttest;

// This is fine, an int passes the static_cast in TemplateTest.

// TemplateTest<bool> booltest;

// This generates a compile time error due to the static cast in the template

// class defined above.

// 9. nullptr

/////////////

//

// nullptr is more strongly typed than 0 or NULL, which helps make things clearer.

// nullptr is not available on GCC at the time of writing so comment out the rest

// of this example to compile with GCC.

int* nullptrtest( nullptr );

if ( nullptrtest == nullptr )

{

// the pointer is null.

std::cout << “The nullptrtest null pointer is set to nullptr.”

<< std::endl;

}

// If we don’t use nullptr, which function is called for our old null pointer

// values?

NullPointerCall( 0 );

NullPointerCall( NULL );

// If we use nullptr instead things are clearer.

NullPointerCall( nullptr );

// 10. initialiser lists

////////////////////////

//

// This is supported by GCC, but not VS, so I’ve commented it out. Obviously

// initialiser lists can also be used to pass data to methods. I think this is one

// of the more useful features of C++11, if only for reasons of clarity.

//std::map< int, int> myuselessnumbermap{ { 1,1 }, { 2, 2 } };

// There are many other C++11 features that haven’t made it into stable versions

// of either VS or GCC, but now that the standard has been ratified I expect they

// will soon follow. Better enum support, in-class member variable initialisation,

// override control and thread support like thread local storage are all useful

// features, there are many more new features that future compilers will support.

// See http://www2.research.att.com/~bs/C++0xFAQ.html for a description of these

// and the unimplemented C++11 features (aka C++0x, they must’ve been a bit late).

return 0;

// Literals in code!! Just this one time I’ll let myself off (anyway, I’ve broken

// most of our other coding standards along the way).

}

// These are the implementations of the nullptr description function calls.

void NullPointerCall( int notAPointerParameter )

{

std::cout << “So you’d like me to process your integer?” << std::endl;

}

void NullPointerCall( int* aPointerParameter )

{

std::cout << “So you’d like me to process your pointer?” << std::endl;

}

More From The Blog

No Results Found

The page you requested could not be found. Try refining your search, or use the navigation above to locate the post.