This chapter concentrates on the new C++11 changes to the C++ language. The book already has covered several C++11 features, and we’ll begin by reviewing them. Then we’ll look at some additional features in some detail. Next, we’ll identify some of the C++11 additions that are beyond the scope of this book. (Given that the C++11 draft is over 80% longer than C++98, we won’t cover everything.) Finally, we’ll briefly examine the BOOST library.
C++11 Features Revisited
By now you may have lost track of the many C++11 changes we already have encountered. This section reviews them briefly.
New Types
C++11 adds the long long and unsigned long long types to support 64-bit integers (or wider) and the char16_t and char32_t types to support 16-bit and 32-bit character representations, respectively. It also adds the “raw” string. Chapter 3, “Dealing with Data,” discusses these additions.
Uniform Initialization
C++11 extends the applicability of the brace-enclosed list (
int x = {5};
double y {2.75};
short quar[5] {4,5,2,76,1};
Also the list-initialization syntax can be used in new expressions:
int * ar = new int [4] {2,4,6,7}; // C++11
With class objects, a braced list can be used instead of a parenthesized list to invoke a constructor:
class Stump
{
private:
int roots;
double weight;
public:
Stump(int r, double w) : roots(r), weight(w) {}
};
Stump s1(3,15.6); // old style
Stump s2{5, 43.4}; // C++11
Stump s3 = {4, 32.1}; // C++11
However, if a class has a constructor whose argument is a std::initializer_list template, then only that constructor can use the list-initialization form. Various aspects of list-initialization were discussed in Chapters 3, 4, 9, 10, and 16.
Narrowing
The initialization-list syntax provides protection against narrowing—that is, against assigning a numeric value to a numeric type not capable of holding that value. Ordinary initialization allows you to do things that may or may not make sense:
char c1 = 1.57e27; // double-to-char, undefined behavior
char c2 = 459585821; // int-to-char, undefined behavior
If you use initialization-list syntax, however, the compiler disallows type conversions that attempt to store values in a type “narrower” than the value:
char c1 {1.57e27}; // double-to-char, compile-time error
char c2 = {459585821};// int-to-char,out of range, compile-time error
However, conversions to wider types are allowed. Also a conversion to a narrower type is allowed if the value is within the range allowed by the type:
char c1 {66}; // int-to-char, in range, allowed
double c2 = {66}; // int-to-double, allowed
std::initializer_list
C++11 provides an initializer_list template class (discussed in Chapter 16, “The string Class and the Standard Template Library”) that can be used as a constructor argument. If a class has such a constructor, the brace syntax can be used only with that constructor. The items in the list should all be of the same type or else be convertible to the same type. The STL containers provide constructors with initializer_list arguments:
vector
vector
vector
The initializer_list header file provides support for this template class. The class has begin() and end() member functions specifying the range of the list. You can use an initializer_list argument for regular functions as well as for constructors:
#include
double sum(std::initializer_list
int main()
{
double total = sum({2.5,3.1,4}); // 4 converted to 4.0
...
}
double sum(std::initializer_list
{
double tot = 0;
for (auto p = il.begin(); p !=il.end(); p++)
tot += *p;
return tot;
}
Declarations
C++11 implements several features that simplify declarations, particularly for situations arising when templates are used.
auto
C++11 strips the keyword auto of its former meaning as a storage class specifier (Chapter 9, “Memory Models and Namespaces”) and puts it to use (Chapter 3) to implement automatic type deduction, provided that an explicit initializer is given. The compiler sets the type of the variable to the type of the initialization value:
auto maton = 112; // maton is type int
auto pt = &maton // pt is type int *
double fm(double, int);
auto pf = fm; // pf is type double (*)(double,int)
The auto keyword can simplify template declarations too. For example, if il is an object of type std::initializer_list
for (std::initializer_list
p !=il.end(); p++)
with this: