The C++ general-purpose programming language has evolved considerably over time. In this article, the author discusses some of the changes brought about by the C++11 and the C++14 standards.
How would you go about choosing a programming language for a new project? You may perhaps opt for a language with object-oriented support, an elegant feature set and a rich library. C++ certainly fits the bill on all counts, but you may still have some doubts about things like memory leaks and limited library support. But all these are misconceptions about C++. With the introduction of recent standards like C++11 and C++14, most of the hurdles associated with this language have been overcome. In this article, we’ll look at a few highlights of the new additions in C++11 and C++14.
C++ standardisation
C++ has undergone rapid standardisation over the past decade, and has become a good choice for developers as well as a good competitor to other languages. As you are aware, C++98 is the first ISO standard. The following revisions were added subsequently, and these are considered as modern C++ (ignoring C++03, which has minor additions).
C++11
C++14
C++17
Many library additions have been introduced in these standards, which became a drop-in replacement for third party libraries like Boost. Even though C++17 is the most recent standard, C++14 can be considered as the baseline due to its matured support from many compilers, and wide adoption in various open source libraries and implementations. The next revision in the pipeline is C++20, a discussion on which may be premature.
Modern C++ features
Lambda expressions: These are often called lambdas and are the most convenient way of representing anonymous functions. Lambdas enable the easiest way of defining call back functions for event handling or error processing. They are similar to anonymous functions added in other languages like Java, JavaScript and Python.
Here are a few examples of lambda expressions:
[] (int x,int y)->int { return x + y; } [] (int x)->bool { return x > 30; } [] (auto x,auto y)->bool { return x>y; }
C++14 allows lambda parameters and can be generically specified by auto keywords. Lambdas can take objects from an enclosing scope by capturing syntax by value or reference:
int tmin=60; [tmin] (int x)->bool { return x>tmin; } [&tmin] (int x)->bool { return x>tmin; }
Lambdas are extremely helpful with STL algorithms (rather than using function pointers or function objects) to provide custom logic.
Here is an example—let’s assume that we have a container mylist that holds certain objects and we want to sort these based on a data member named id. You can try the following function call to do so:
std::sort(mylist.begin(), mylist.end(), [](auto r1,auto r2)->bool { return r1.getId() > r2.getId(); });
Move semantics: Move semantics ensure that underlying resources are moved from one object to another, i.e., by detaching from a source and attaching to a destination. This is quite justified at certain times as we don’t want to keep multiple copies of resources. This is also in contrast with the copy constructor, which duplicates resources. Do note that the term ‘move’ is applicable to the underlying resources behind the objects and not to the object memory itself. With move semantics, classes are facilitated with the move constructor, move operator = and rvalue references.
For example, let’s assume that the MyArray class holds the following members:
- m_arr: base address of internal array of int type
- m_len: length of the array
Here is a code snippet that shows the difference between the copy and move constructors. The move constructor is specified with the rvalue reference, which is indicated by ‘&&’.
MyArray(const MyArray& cref): m_len(cref.m_len) { m_arr=new int[m_len]; for(int i=0;i<m_len;i++) { m_arr[i]=cref.m_arr[i]; } } MyArray(MyArray&& mref): m_len(mref.m_len), m_arr(mref.m_arr) { mref.m_len=0; mref.m_arr=nulptr; //array is detached from source and attached to destination } MyArray a2(a1); //invokes copy constructor MyArray a3(std::move(a1)); //invokes move constructor
With move semantics, STL containers provide MoveInsertables taking rvalue references, where objects can be moved (of course, the underlying resources) from the calling scope to the container. In the following code snippet, MyObject contents are moved from the calling scope to the vector. By default, unnamed objects are compatible with rvalue references, whereas named objects need to be wrapped in std::move.
std::vector<MyObject> data; MyObject o1(args); data.push_back(std::move(01)); data.push_back(MyObject(args));
Move semantics are also helpful when returning objects, to avoid multiple copies of underlying resources. In the following code snippet, getInstance is returning a temporary object, which is efficiently moved to rvalue reference rr, rather than copying the resource:
MyArray getInstance(int len) { int arr[len]; //fill arr with random values return MyArray(arr,len); //anonymous object } MyArray &&rr=getInstance(); //this avoids duplication of internal array
STL additions
Here are some major additions to the STL collection.
- std::array – This is a fixed-sized sequential container, which is allocated on the stack, and its performance is on par with the C Style raw array, when compared to std::vector.
- std::function – This is a convenient polymorphic wrapper for callable elements like normal functions, lambdas, function objects, etc. Instances of std::function are a great fit to define call backs with better type safety.
- std::bind – This returns a function object by minimising the number of arguments required for existing callable elements by fixing certain arguments through placeholders.
- A few containers based on hash tables have been added, which are std::unordered_set, std::unordered_multiset, and std::unordered_map, std::unordered_multimap.
Smart pointers
In C++11, auto_ptr has been deprecated and the following smart pointers added.
- std::unique_ptr: The destructors of this class release memory for the underlying object when the pointer goes out of scope. Through move semantics, it is ensured that only one pointer can bind to the object at a time.
- std::shared_ptr: Multiple pointers can hold the object, which will be destroyed when the last pointer goes out of the scope.
- make_unique,make_shared has been added in C++14 for convenient creation.
General libraries and utilities
- std::regex – Regular expression library
- std::chrono – Date and time utilities
- std::tuple – Encapsulation of heterogeneous elements for a fixed dimension
- Random number generators
Concurrency and IPC support
The following classes have been added to enable concurrency, atomic operations and to achieve synchronisation. In the Linux environment, the use of these classes depends on the pthread library. Do add the linker flag, -lpthread option to g++ while building the code.
- std::thread
- std::mutex
- std::atomic
- std::condition_variable
- std::future
Let’s consider an example on how to create a simple thread.
void printnums(int n) { //some code } std::thread t1( [] { printnums(10) }); std::thread t2( std::bind(printnums,10)); int n=10; std::thread t3( [n]() { for(int i=0;i<n;i++) std::cout << i << “\n”; }); t1.join(); t2.join(); t3.join();
Random bytes
Here is a list of a few random features added as language extensions:
- Defining truly compile time known literals with constexpr, which improves ROMability of the code
- Automatic type deduction with auto keyword and decltype
- Better aliasing with using keyword
- Raw string literals, e.g., useful to specify JSON content
- nullptr keyword in place of NULL
- Range based for loops for convenient iteration
- static_assert for compile time assertions
- enum class — strongly typed/scoped enums
- Binary literals, digit separators, user defined literals
- =default, =delete controls on constructors, destructors, operators
- In-class initialisation of data members
- Delegating constructors
- std::initializer_list to initialise objects in array style
- Strict initialisers with {} to prevent narrowing conversions
- Override, final keywords with inheritance
- Explicit user-defined type conversions
- External templates, template aliases, variadic templates
- noexcept keyword to indicate whether a function is throwing exceptions or not
Compiler support
Most of the compilers have been migrated with C++14 support. Here is the status of two popular open source compiler collections.
- gcc/g++ : gcc 4.8.1 is the first release with complete support for C++11. C++14 is the default standard from gcc 6.1. We can enable these standards in earlier editions with the help of compiler options -std=c++11 and -std=c++14.
- Cross-compilers based on GCC have the same support as native compilers.
- llvm clang: C++11 support has been added in Clang 3.3 and C++14 support has been added in Clang 3.4.
Coding standards
The following standards have added support for C++11 and C++14 in their recent versions:
- SEI CERT C++ coding standard 2016
- PRQA HIC++ 4.0
- Google C++ style guide
- AUTOSAR C++14 guidelines
- C++ core guidelines
Open source libraries and implementations
The following open source libraries have adopted the modern standards.
- Qt, a cross-platform UI design framework has good enablement for C++11 from Qt 5.x. From Qt 5.7 onwards, modules have also been written using C++11 features, so a compatible compiler is required to build from sources for the latest versions. It’s very easy to write event handlers (slots) with lambda expressions.
- Asio: This is a networking library that works well with C++11 without the need for boost libraries.
- Many JSON libraries have been migrated to C++11, e.g., https://github.com/nlohmann/json.
- Robotic Operating System (ROS) uses C++14 in the latest Melodic release.
- IoTivity is a reference implementation of OCF specification standards.
- POCO libraries
This is just a small list, though there are numerous other libraries and implementations that have been migrated to the latest standards. This article gives readers a glimpse of modern C++ features and additions. Please refer to the following resources for more details and to appreciate the true power of C++ programming.
Thank you for the tutorial