C++0X features
Auto
template<class T> void printall(const vector<T>& v)
{
for (typename vector<T>::const_iterator p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}
template<class T> void printall(const vector<T>& v)
{
for (auto p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}
When the type of a variable depends critically on template argument it can
be really hard to write code without auto. For example:
template<class T, class U> void (const vector<T>& vt, const vector<U>& vu)
{
// ...
auto tmp = vt[i]*vu[i];
// ...
}
Range for
A range for statement allows you to iterate through a "range",
which is anything you can iterate through like an STL-sequence
defined by a begin() and end(). All standard containers can be
used as a range, as can a std::string, an initializer list, an
array, and anything for which you define begin() and end(), e.g.
an istream. For example:
void f(const vector<double>& v)
{
for (auto x : v) cout << x << '\n';
for (auto& x : v) ++x; // using a reference to allow us to chage the value
}
You can read that as "for all x in v" going through starting with
v.begin() and iterating to v.end(). Another example:
for (const auto x : { 1,2,3,5,8,13,21,34 }) cout << x << '\n';
The begin() (and end()) can be a member to be called x.begin()
or a free-standing function to be called begin(x).
Default and delete functions
class X
{
// ...
X& operator=(const X&) = delete; // Disallow copying
X(const X&) = delete;
};
Conversely, we can also say explicitly that we want to default
copy behavior:
class Y
{
// ...
Y& operator=(const Y&) = default; // default copy semantics
Y(const Y&) = default;
};
Enum class
enum Alert { green, yellow, election, red }; // traditional enum
enum class Color { red, blue }; // scoped and strongly typed enum
// no export of enumerator names into enclosing scope
// no implicit conversion to int
enum class TrafficLight { red, yellow, green };
Alert a = 7; // error (as ever in C++)
Color c = 7; // error: no int->Color conversion
int a2 = red; // ok: Alert->int conversion
int a3 = Alert::red; // error in C++98; ok in C++0x
int a4 = blue; // error: blue not in scope
int a5 = Color::blue; // error: not Color->int conversion
Color a6 = Color::blue; // ok
enum class Color : char { red, blue }; // compact representation
enum class TrafficLight { red, yellow, green }; // by default, the underlying type is int
enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U }; // how big is an E?
// (whatever the old rules say;
// i.e. "implementation defined")
enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U }; // now we can be specific
It also enables forward declaration of enums:
enum class Color_code : char; // (forward) declaration
void foobar(Color_code* p); // use of forward declaration
// ...
enum class Color_code : char { red, yellow, green, blue }; // definition
Constexpr
enum Flags { good=0, fail=1, bad=2, eof=4 };
constexpr int operator|(Flags f1, Flags f2) { return Flags(int(f1)|int(f2)); }
void f(Flags x)
{
switch (x) {
case bad: /* ... */ break;
case eof: /* ... */ break;
case bad|eof: /* ... */ break;
default: /* ... */ break;
}
}
Decltype
decltype -- the type of an expression
decltype(E) is the type ("declared type") of the name or expression E
and can be used in declarations. For example:
void f(const vector<int>& a, vector<float>& b)
{
typedef decltype(a[0]*b[0]) Tmp;
for (int i=0; i<b.size(); ++i) {
Tmp* p = new Tmp(a[i]*b[i]);
// ...
}
// ...
}
Initializer list
vector<double> v = { 1, 2, 3.456, 99.99 };
list<pair<string,string>> languages = {
{"Nygaard","Simula"}, {"Richards","BCPL"}, {"Ritchie","C"}
};
map<vector<string>,vector<int>> years = {
{ {"Maurice","Vincent", "Wilkes"},{1913, 1945, 1951, 1967, 2000} },
{ {"Martin", "Ritchards"} {1982, 2003, 2007} },
{ {"David", "John", "Wheeler"}, {1927, 1947, 1951, 2004} }
};
Delegating constructors
In C++98, if you want two constructors to do the same thing, repeat yourself or call "an init() function." For example:
class X {
int a;
validate(int x) { if (0<x && x<=max) a=x; else throw bad_X(x); }
public:
X(int x) { validate(x); }
X() { validate(42); }
X(string s) { int x = lexical_cast<int>(s); validate(x); }
// ...
};
Verbosity hinders readability and repetition is error-prone. Both get in the way of maintainability. So, in C++0x, we can define one constructor in terms of another:
class X {
int a;
public:
X(int x) { if (0<x && x<=max) a=x; else throw bad_X(x); }
X() :X{42} { }
X(string s) :X{lexical_cast<int>(s)} { }
// ...
};
Use constructors
class Derived : public Base {
public:
using Base::f; // lift Base's f into Derived's scope -- works in C++98
void f(char); // provide a new f
void f(int); // prefer this f to Base::f(int)
using Base::Base; // lift Base constructors Derived's scope -- C++0x only
Derived(char); // provide a new constructor
Derived(int); // prefer this constructor to Base::Base(int)
// ...
};
Static assert
int f(int* p, int n)
{
static_assert(p==0,"p is not null"); // error: static_assert() expression not a constant expression
// ...
}
Nullptr
char* p = nullptr;
void f(int);
void f(char*);
f(0); // call f(int)
f(nullptr); // call f(char*)
Variadic templates
template<class ... Types>
void f(Types ... args); // variadic template function
// (i.e. a function that can take an arbitrary number of arguments of arbitrary types)
f(); // OK: args contains no arguments
f(1); // OK: args contains one argument: int
f(2, 1.0); // OK: args contains two arguments: int and double
Rvalue reference
template<class T> class vector {
// ...
vector(const vector&); // copy constructor
vector(vector&&); // move constructor
vector& operator=(const vector&); // copy assignment
vector& operator=(vector&&); // move assignment
}; // note: move constructor and move assignment takes non-const &&
// they can, and usually do, write to their argument
The && indicates an "rvalue reference". An rvalue reference can bind to
an rvalue (but not to an lvalue):
X a;
X f();
X& r1 = a; // bind r1 to a (an lvalue)
X& r2 = f(); // error: f() is an rvalue; can't bind
X&& rr1 = f(); // fine: bind rr1 to temporary
X&& rr2 = a; // error: bind a is an lvalue
template<class T>
void swap(T& a, T& b) // "perfect swap" (almost)
{
T tmp = move(a); // could invalidate a
a = move(b); // could invalidate b
b = move(tmp); // could invalidate tmp
}
User defined literals
constexpr complex<double> operator "" i(long double d) // imaginary literal
{
return {0,d}; // complex is a literal type
}
Lambda
vector<int> v = {50, -10, 20, -30};
std::sort(v.begin(), v.end()); // the default sort
// now v should be { -30, -10, 20, 50 }
// sort by absolute value:
std::sort(v.begin(), v.end(), [](int a, int b) { return abs(a)<abs(b); });
// now v should be { -10, 20, -30, 50 }
A lambda expression can access local variables in the scope in
which it is used. For example:
void f(vector<Record>& v)
{
vector<int> indices(v.size());
int count = 0;
fill(indices.begin(),indices.end(),[&count](){ return ++count; });
// sort indices in the order determined by the name field of the records:
std::sort(indices.begin(), indices.end(), [&](int a, int b) { return v[a].name<v[b].name; });
// ...
}
Threads
#include<thread>
void f();
struct F {
void operator()();
};
int main()
{
std::thread t1{f}; // f() executes in separate thread
std::thread t2{F()}; // F()() executes in separate thread
}
int main()
{
std::thread t1{f}; // f() executes in separate thread
std::thread t2{F()}; // F()() executes in separate thread
t1.join(); // wait for t1
t2.join(); // wait for t2
}