Modified August 25, 2009
This document is written by and maintained by Bjarne Stroustrup. Constructive comments, correction, references, and suggestions are of course most welcome. Currently, I'm working to improve completeness and clean up the references.
C++0x is the next ISO C++ standard. Currently a draft is available for comments. The previous (and current) standard is often referred to as C++98 or C++03; the differences between C++98 and C++03 are so few and so technical that they ought not concern users.
The standard is expected to be ready for final national votes in 2010 -- yielding C++10 or C++11 even if the ISO bureaucracy takes some time work through its formalities. The name "C++0x" is a relict of the days where I and others, hoped for a C++08 or C++09. However, to minimize confusion, I'll keep referring to the upcoming C++ standard with the feature set defined here as C++0x. Think of 'x' as hexadecimal.
If you have comments on C++0x, please find some member of your national standards body -- or a member of any standards body -- to send your comments to. That's now the only way and will ensure that the committee doesn't have to deal with many very similar comment. Remember, the committee consists of volunteers with limited time and resources.
All official documents relating to C++0x can be found at the ISO C++ committee's website. The official name of the committee is SC22 WG21.
Caveat: This FAQ will be under construction for quite a while. Comments, questions, references, corrections, and suggestions welcome.
Here are some high-level questions
Questions about individual language features can be found here:
Questions about individual standard library facilities can be found here:
Below are questions to specific questions as indexed above.
My ideal is to use programming language facilities to help programmers think differently about system design and implementation. I think C++0x can do that - and do it not just for C++ programmers but for programmers used to a variety of modern programming languages in the general and very broad area of systems programming.
In other words, I'm still an optimist.
By October 2009, we should know exactly what the new standard will look like and then we can discuss whether that language should be referred to as C++10 or C++0B or C++oxC or whatever. Personally, I prefer plain C++ and to use a year marker only when I need to distinguish it from previous versions of C++, such as ARM C++, C++98 and C++03. For now, I bow to convention and still use C++0x for the next version. Think of 'x' as hexadecimal.
I expect more and more features to become available with each new release. Most likely, relatively isolated features, such as auto, lambda, and strongly typed enums, to be among the first available. I do not care to guess when every compiler will provide all of C++0x -- undoubtedly, that will take years -- but I note that every C++0x feature has been implemented by someone somewhere so there is implementation experience available for implementers to rely on.
To try to select rationally from the flood of suggestions we devised a set of specific design aims. We couldn't completely follow them and they weren't sufficiently complete to guide the committee in every detail (and IMO couldn't possible be that complete).
The result has been a language with greatly improved abstraction mechanisms. The range of abstractions that C++ can express elegantly, flexibly, and at zero costs compared to hand-crafted specialized code has greatly increased. When we say "abstraction" people often just think "classes" or "objects." C++0x goes far beyond that: The range of user-defined types that can be cleanly and safely expressed has grown with the addition of features such as initializer-lists, uniform initialization, template aliases, rvalue references, defaulted and deleted functions, and variadic templates. Their implementation eased with features, such as auto, inherited constructors, and decltype. These enhancements are sufficient to make C++0x feel like a new language.
For a list of accepted language features, see the feature list
For a list of accepted libraries, see the library component list.
For more details see:
Another way of looking at detailed aims is to look at areas of use and styles of usage:
Yes:
Many countries have national standards bodies with active C++ groups. These groups hold meetings, coordinate over the web, and some send representatives to the ISO meetings. Canada, France, Germany, Switzerland, UK, and USA are present at most meetings. Denmark, the Netherlands, Japan, Norway, Spain, and others are represented in person less frequently.
Much of the work goes on in-between meetings over the web and the results are recorded as numbered committee papers on the WG21 website.
The committee meets two to three times a year for a week each time. Most work at those meetings are in sub-working groups, such as "Core", "Library", "Evolution", and "Concurrency." As needed, there are also in-between meetings of ad-hoc working groups on specific urgent topics, such as "concepts" and "memory model." Voting takes place at the main meetings. First, working groups hold "straw votes" to see if an issue is ready for presentation to the committee as a whole. Then, the committee as a whole votes (one member one vote) and if something is accepted the nations vote. We take great care that we do not get into a situation where the majority present and the nations disagrees -- proceeding if that is the case would guarantee long-term controvercy. Final votes on official drafts are done by mail by the national standards bodies.
The committee has formal liaison with the C standards group (SC22 WG14) and POSIX, and more or less formal contacts with several other groups.
Naturally, many (but not all) of these volunteers have day jobs focused on C++: We have compiler writers, tool builders, library writers, application builders (too few of those), researchers (only a few), consultants, test-suite builders, and more.
Here is a very abbreviated list of organizations involved: Adobe, Apple, Boost, EDG, Google, HP, IBM, Intel, Microsoft, Red Hat, Sun.
Here is a short list of names of members who you may have encountered in the literature or on the web: Dave Abrahams, Matt Austern, Pete Becker, Hans Boehm, Steve Clamage, Lawrence Crowl, Beman Dawes, Doug Gregor, Howard Hinnant, Jaakko Jarvi, Francis Glassborow, Jens Maurer, Jason Merrill, Sean Parent, P.J. Plauger, Tom Plum, Gabriel Dos Reis, Bjarne Stroustrup, Herb Sutter, David Vandervoorde Michael Wong. Apologies to the 200+ current and past members that I couldn't list. Also, please note the author lists on the various papers: a standard is written by (many) individuals, not by an anonymous committee.
You can get a better impression of the breath and depth of expertise involved by examining the author lists on the WG21 papers, but please remember there are major contributors to the standards effort who do not write a lot.
Since it is likely that "concepts" in some form or other will be part of a later version of C++, I have not deleted the concept sections from this document, but left them at the end:
auto x = 7;Here x will have the type int because that's the type of its initializer. In general, we can write
auto x = expression;and the type of x will be the type of the value computed from "expression".
The use of auto to deduce the type of a variable from its initializer is obviously most useful when that type is either hard to know exactly or hard to write. Consider:
template<class T> void printall(const vector<T>& v)
{
for (auto p = v.begin(); p!=v.end(); ++p) cout << *p << "\n";
}
In C++98, we'd have to write
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";
}
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];
// ...
}
The type of tmp should be what you get from multiplying a T by a U,
but exactly what that is can be hard
for the human reader to figure out,
but of course the compiler knows once it has figured out what particular T and U
it is dealing with.
The auto feature has the distinction to be the earliest to be suggested and implemented: I had it working in my Cfront implementation in early 1984, but was forced to take it out because of C compatibility problems. Those compatibility problems disappeared when C++98 and C99 accepted the removal of "implicit int"; that is, both languages require every variable and function to be defined with an explicit type. The old meaning of auto ("this is a local variable") is redundant and unused. Several committee members trawled through millions of lines of code finding only a handful of uses -- and most of those were in test suites or appeared to be bugs.
Being primarily a facility to simplify notation in code, auto does not affect the standard library specification.
See also
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).
See also
list<vector<string>> lvs;In C++98 this is a syntax error because there is no space between the two >s. C++0x recognizes such > as a correct termination of two template argument lists.
Why was this ever a problem? A compiler front-end is organized parses/stages. This is about the simplest model:
See also
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;
}
};
Being explicit about the default is obviously redundant, but comments to that effect and (worse)
a user explicitly defining copy operations meant to give the default behavior are not uncommon.
Leaving it to the compiler to implement the default behavior is simpler, less error-prone,
and often leads to better object code.
The "default" mechanism can be used for any function that has a default. The "delete" mechanism can be used for any function. For example, we can eliminate an undesired conversion like this:
struct Z {
// ...
Z(long long); // can initialize with an long long
Z(long) = delete; // but not anything less
};
See also
enum classs ("strong enums") are strongly typed and scoped:
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
As shown, traditional enums work as usual,
but you can now optionally qualify with the enum name.
The new enums are "enum class" because they combine aspects of traditional enumerations (names values) with aspects of classes (scoped members and absense of conversions).
Being able to specify the underlying type allow simpler interoperability and guaranteed sizes of enumerations:
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
The underlying type must be one of the signed or unsigned integer types; the default is int.
In the standard library, enum classes is used
See also
enum Flags { good=0, fail=1, bad=2, eof=4 };
constexpr int operator|(Flags f1, Flags f2) { return Flags(f1|f2); }
void f(Flags x)
{
switch (x) {
case bad: /* ... */ break;
case eof: /* ... */ break;
case bad|eof: /* ... */ break;
default: /* ... */ break;
}
}
Here constexpr says that the function must be of a simple form so that it can be evaluated
at compile time if given constant expressions arguments.
In addition to be able to evaluate expressions at compile time, we want to be able to require expressions to be evaluated at compile time; constexpr in front of a variable definition does that (and implies const):
constexpr int x1 = bad|eof; // ok
void f(Flags f3)
{
constexpr int x2 = bad|f3; // error: can't evaluate at compile time
int x3 = bad|f3; // ok
}
Typically we want the compile-time evaluation guarantee for global or namespace objects, often for
objects we want to place in read-only storage.
This also works for objects for which the constructors are simple enough to be constexpr and expressions involving such objects:
struct Point {
int x,y;
constexpr Point(int xx, int yy) : x(xx), y(yy) { }
};
constexpr Point origo(0,0);
constexpr int z = origo.x;
constexpr Point a[] = {Point(0,0), Point(1,1), Point(2,2) };
constexpr x = a[1].x; // x becomes 1
See also
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]);
// ...
}
// ...
}
This notion has been popular in generic programming under the label "typeof" for a long time,
but the typeof implementations in actual use were incomplete and incompatible,
so the standard version is named decltype.
If you just need the type for a variable that you are about to initialize auto is often a simpler choice. You really need decltype if you need a type for something that is not a variable, such as a return type.
See also
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} }
};
Initializer lists are not just for arrays any more.
The mechanism for accepting a {}-list is a function (often a constructor)
accepting an argument of type std::initializer_list<T>. For example:
void f(initializer_list<int>);
f({1,2});
f({23,345,4567,56789});
f({}); // the empty list
f{1,2}; // error: function call ( ) missing
years.insert({{"Bjarne","Stroustrup"},{1950, 1975, 1985}});
The initializer list can be arbitrary length, but must be homogeneous
(all elements of a the template argument type, T, or convertable to T).
A container might implement an initializer-list constructor like this:
template<class E> class vector {
public:
vector (std::initializer_list<E> s) // initializer-list constructor
{
reserve(s.size()); // get the right amount of space
uninitialized_copy(s.begin(), s.end(), elem); // initialize elements (in elem[0:s.size()))
sz = s.size(); // set vector size
}
// ... as before ...
};
The distinction between direct initialization and copy initialization is maintained for {}-initialization, but becomes relevant less frequently because of {}-initialization. For example, std::vector has an explicit constructor from int and an initializer-list constructor:
vector<double> v1(7); // ok: v1 has 7 elements v1 = 9; // error: no conversion from int to vectorvector<double> v2 = 9; // error: no conversion from int to vector void f(const vector<double>&); f(9); // error: no conversion from int to vector vector<double> v1{7}; // ok: v1 has 1 element (with its value 7) v1 = {9}; // ok v1 now has 1 element (with its value 9) vector<double> v2 = {9}; // ok: v2 has 1 element (with its value 9) f({9}); // ok: f is called withthe list { 9 } vector<vector<double>> vs = { vector<double>(10), // ok: explicit construction (10 elements) vector<double>{10}, // ok explicit construction (1 element with the value 10) 10 // error: vector's constructor is explicit };
The function can access the initializer_list as an immutable sequence. For example:
void f(initializer_list<int> args)
{
for (auto p=args.begin(); p!=args.end(); ++p) cout << *p << "\n";
}
A constructor that takes a single argument of type std::initializer_list is called an initializer-list constructor.
The standard library containers, string, and regex have initializer-list constructors, assignment, etc. An initializer-list can be used as a Range, e.g. in a range for statement
The initializer lists are part of the scheme for uniform and general initialization.
See also
int x = 7.3; // Ouch! void f(int); f(7.3); // Ouch!However, in C++0x, {} initialization doesn't narrow:
int x1 = {7.3}; // error: narrowing
double d = 7;
int x2{d}; // error: narrowing (double to int)
char x3{7}; // ok: even though 7 is an int, this is not narrowing
vector<int> vi = { 1, 2.3, 4, 5.6 }; // error: double to int narrowing
The way C++0x avoids a lot of incompatibilities is by relying on the actual values of initializers
(such as 7 in the example above)
when it can (and not just type) when deciding what is a narrowing conversion.
If a value can be represented exactly as the target type, the conversion is not narrowing.
Note that floating-point to integer conversions are always considered narrowing -- even 7.0 to 7.
See also
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)} { }
// ...
};
See also
int var = 7;
class X {
static const int m1 = 7; // ok
const int m2 = 7; // error: not static
static int m3 = 7; // error: not const
static const int m4 = var; // error: initializer not constant expression
static const string m5 = "odd"; // error: not integral type
// ...
};
The basic idea for C++0x is to allow a non-static data member to be initialized where
it is declared (in its class).
A constructor can then use the initializer when run-time initialization is needed.
Consider:
class A {
public:
int a = 7;
};
This is equivalent to:
class A {
public:
int a;
A() : a(7) {}
};
This saves a bit of typing, but the real benefits come in classes with multiple constructors.
Often, all constructors use a common initializer for a member:
class A {
public:
A(): a(7), b(5), hash_algorithm("MD5"), s("Constructor run") {}
A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run") {}
A(D d) : a(7), b(g(d)), hash_algorithm("MD5"), s("Constructor run") {}
int a, b;
private:
HashingFunction hash_algorithm; // Cryptographic hash to be applied to all A instances
std::string s; // String indicating state in object lifecycle
};
The fact that hash_algorithm and s each has a single default is lost in the mess of code and could
easily become a problem during maintenance.
Instead, we can factor out the initialization of the data members:
class A {
public:
A(): a(7), b(5) {}
A(int a_val) : a(a_val), b(5) {}
A(D d) : a(7), b(g(d)) {}
int a, b;
private:
HashingFunction hash_algorithm{"MD5"}; // Cryptographic hash to be applied to all A instances
std::string s{"Constructor run"}; // String indicating state in object lifecycle
};
If a member is initialized by both an in-class initializer and a constructor, only the constructor's
initialization is done (it "overrides" the default). So we can simplify further:
class A {
public:
A() {}
A(int a_val) : a(a_val) {}
A(D d) : b(g(d)) {}
int a = 7;
int b = 5;
private:
HashingFunction hash_algorithm{"MD5"}; // Cryptographic hash to be applied to all A instances
std::string s{"Constructor run"}; // String indicating state in object lifecycle
};
See also
struct B {
void f(double);
};
struct D : B {
void f(int);
};
B b; b.f(4.5); // fine
D d; d.f(4.5); // surprise: calls f(int) with argument 4
In C++98, we can "lift" a set of overloaded functions from a base class into a derived class:
struct B {
void f(double);
};
struct D : B {
using B::f; // bring all f()s from B into scope
void f(int); // add a new f()
};
B b; b.f(4.5); // fine
D d; d.f(4.5); // fine: calls D::f(double) which is B::f(double)
I have said that "Little more than a historical accident prevents using this to work for a constructor
as well as for an ordinary member function."
C++0x provides that facility:
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);
// ...
};
If you so choose, you can still shoot yourself in the foot by inheriting constructors in a derived class in which you define new member variables needing initialization:
struct B1 {
B1(int) { }
};
struct D1 : B1 {
using B1::B1; // implicitly declares D1(int)
int x;
};
void test()
{
D1 d(6); // Oops: d.x is not initialized
D1 e; // error: D1 has no default constructor
}
See also
static_assert(expression,string);The compiler evaluates the expression and writes the string as an error message if the expression is false (i.e., if the assertion failed). For example:
static_assert(sizeof(long) >= 8, "64-bit code generation required for this library.");
struct S { X m1; Y m2; };
static_assert(sizeof(S)==sizeof(X)+sizeof(Y),"unexpected padding in S");
A static_assert can be useful to make assumptions about a program and its treatment by a compiler explicit.
Note that since static_assert is evaluated at compile time, it cannot be used to check assumptions that
depends on run-time values. For example:
int f(int* p, int n)
{
static_assert(p==0,"p is not null"); // error: static_assert() expression not a constant expression
// ...
}
(instead, test and throw an exception in case of failure).
See also
long long x = 9223372036854775807LL;No, there are no long long longs nor can long be spelled short long long.
See also
char* p = nullptr; int* q = nullptr; char* p2 = 0; // 0 still works and p==p2 void f(int); void f(char*); f(0); // call f(int) f(nullptr); // call f(char*) void g(int); g(nullptr); // error: nullptr is not an int int i = nullptr; // error nullptr is not an int
See also
template<class T, class U>
??? mul(T x, U y)
{
return x*y;
}
What can we write as the return type? It's "the type of x*y",
of course, but how can we say that
First idea, use decltype:
template<class T, class U>
decltype(x*y) mul(T x, U y) // scope problem!
{
return x*y;
}
That won't work because x and y are not in scope.
However, we can write:
template<class T, class U>
decltype(*(T*)(0)**(U*)(0)) mul(T x, U y) // ugly! and error prone
{
return x*y;
}
However, calling that "not pretty" would be overly polite.
The solution is put the return type where it belongs, after the arguments:
template<class T, class U>
[] mul(T x, U y) -> decltype(x*y)
{
return x*y;
}
We use the notation [] that we use to introduce
lambdas to mean "return type to be deduced or specified later."
Since that function body consists of just a return statement, we can eliminate the decltype
and reduce the function definition to
template<class T, class U>
[] mul(T x, U y) { return x*y; }
The suffix syntax is not primarily about templates and type deduction, it is really about scope.
struct List {
struct Link { /* ... */ };
Link* erase(Link* p); // remove p and return the link before p
// ...
};
List::Link* List::erase(Link* p) { /* ... */ }
The first List:: is necessary only because the scope of List isn't entered
until the second List::.
Better:
[] List::erase(Link* p) -> Link* { /* ... */ }
Now neither Link needs explicit qualification.
See also
template<class T>
using Vec = std::vector<T,My_alloc<T>>; // standard vector using my allocator
Vec<int> fib = { 1, 2, 3, 5, 8, 13 }; // allocates elements using My_alloc
vector<int,My_alloc<int>> verbose = fib; // verbose and fib are of the same type
The keyword using is used to get a linear notation "name followed by what it refers to."
We tried with the conventional and convoluted typedef solution, but never managed to get
a complete and coherent solution until we settled on a less obscure syntax.
Specialization works
(you can alias a set of specializations but you cannot specialize an alias)
For example:
template<int>
struct int_exact_traits { // idea: int_exact_trait<N>::type is a type with exactly N bits
typedef int type;
};
template<>
struct int_exact_traits<8> {
typedef char type;
};
template<>
struct int_exact_traits<16> {
typedef char[2] type;
};
// ...
template<int N>
using int_exact = typename int_exact_traits<N>::type; // define alias for convenient notation
int_exact<8> a = 7; // int_exact<8> is an int with 8 bits
In addition to being important in connection with templates, type aliases can also be used as a different
(and IMO better) syntax for ordinary type aliases:
typedef void (*PFD)(double); // C style using PF = void (*)(double); // using plus C-style type using P = [](double)->void; // using plus suffix return type
See also
Here is an example (from "A brief introduction to Variadic templates'' (see references)) implementing a general, type-safe, printf(). It would probably be better to use boost::format, but consider:
const string pi = "pi"; const char* m = "The value of %s is about %g (unless you live in %s).\n"; printf(m, pi, 3.14159, "Indiana");The simplest case of printf() is when there are no arguments except the format string, so we'll handle that first:
void printf(const char* s)
{
while (s) {
if (*s=='%' && *++s!='%') // make sure that there wasn't meant to be more arguments
// %% represents plain % in a format string
throw runtime_error("invalid format: missing arguments");
std::cout << *s++;
}
}
That done, we must handle printf() with more arguments:
template<typename T, typename... Args> // note the "..."
void printf(const char* s, T value, Args... args) // note the "..."
{
while (s) {
if (*s=='%' && *++s!='%') { // a format specifier (ignore which one it is)
std::cout << value; // use first non-format argument
return printf(++s, args...); // "peel off" first argument
}
std::cout << *s++;
}
throw std::runtime error("extra arguments provided to printf");
}
This code simply ``peels off'' the first non-format argument and then calls itself recursively.
When there are no more non-format arguments, it calls the first (simpler) printf() (above).
This is rather standard functional programming done at compile time.
Note how the overloading of << replaces the use of the (possibly erroneous) ``hint'' in the format specifier.
The Args...defines what is called a ``parameter pack.'' That's basically a sequence of (type/value) pairs from which you can ``peel off'' arguments starting with the first. When printf() is called with one argument, the first definition (printf(const char*)) is chosen. When printf() is called with two or more arguments, the second definition (printf(const char*, T value, Args... args)) is chosen, with the first argument as s, the second as value, and the rest (if any) bundled into the parameter pack args for later use. In the call
printf(++s, args...);The parameter pack args is expanded so that the next argument can now be selected as value. This carries on until args is empty (so that the first printf() is called).
If you are familiar with functional programming, you should find this an unusual notation for a pretty standard technique. If not, here are some small technical examples that might help. First we can declare and use a simple variadic template function (just like printf() above):
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 doubleWe can build a variadic type:
template<typename Head, typename... Tail>
class tuple<Head, Tail...>
: private tuple<Tail...> { // here is the recursion
// Basically, a tuple stores its head (first (type/value) pair
// and derives from the tuple of its tail (the rest ofthe (type/value) pairs.
// Note that the type is encoded in the type, not stored as data
typedef tuple<Tail...> inherited;
public:
tuple() { } // default: the empty tuple
// Construct tuple from separate arguments:
tuple(typename add_const_reference<Head>::type v, typename add_const_reference<Tail>::type... vtail)
: m_head(v), inherited(vtail...) { }
// Construct tuple from another tuple:
template<typename... VValues>
tuple(const tuple<VValues...>& other)
: m_head(other.head()), inherited(other.tail()) { }
template<typename... VValues>
tuple& operator=(const tuple<VValues...>& other) // assignment
{
m_head = other.head();
tail() = other.tail();
return *this;
}
typename add_reference<Head>::type head() { return m_head; }
typename add_reference<const Head>::type head() const { return m_head; }
inherited& tail() { return *this; }
const inherited& tail() const { return *this; }
protected:
Head m_head;
}
Given that definition, we can make tuples (and copy and manipulate them):
tuple<string,vectorIt can get a bit tedious to mention all of those types, so often, we deduce them from argument types, e.g. using the standard library make_tuple():,double> tt("hello",{1,2,3,4},1.2); string h = tt.head(); // "hello" tuple<vector<int>,double> t2 = tt.tail(); // {{1,2,3,4},1.2};
template<class... Types>
tuple<Types...> make_tuple(Types&&... t) // this definition is somewhat simplified (see standard 20.5.2.2)
{
return tuple<Types...>(t);
}
string s = "Hello";
vector<int> v = {1,22,3,4,5};
auto x = make_tuple(s,v,1.2);
See also:
string a[] = { "foo", " bar" }; // ok: initialize array variable
vector<string> v = { "foo", " bar" }; // error: initializer list for non-aggregate vector
void f(string a[]);
f( { "foo", " bar" } ); // syntax error: block as argument
and
int a = 2; // "assignment style"
int[] aa = { 2, 3 }; // assignment style with list
complex z(1,2); // "functional style" initialization
x = Ptr(y); // "functional style" for conversion/cast/construction
and
int a(1); // variable definition int b(); // function declaration int b(foo); // variable definition or function declarationIt can be hard to remember the rules for initialization and to choose the best way.
The C++0x solution is to allow {}-initializer lists for all initialization:
X x1 = X{1,2};
X x2 = {1,2}; // the = is optional
X x3{1,2};
X* p = new X{1,2};
struct D : X {
D(int x, int y) :X{x,y} { /* ... */ };
};
struct S {
int a[3];
S(int x, int y, int z) :a{x,y,z} { /* ... */ }; // solution to old problem
};
Importantly, X { a } constructs the same value in every context,
so that { } initialization gives the same result in all places where it is legal.
For example:
X x{a};
X* p = new X{a};
z = X{a}; // use as cast
f({a}); // function argument (of type X)
return {a}; // function return value (function returning X)
See also
void incr(int& a) { ++a; }
int i = 0;
incr(i); // i becomes 1
incr(0); // error: 0 in not an lvalue
If that incr(0) were allowed either some temporary that nobody ever saw would be incremented or - far worse -
the value of 0 would become 1.
The latter sounds silly, but there was actually a bug like that in early Fortran compilers that set aside a memory
location to hold the value 0.
So far, so good, but consider
template<class T> swap(T& a, T& b) // "old style swap"
{
T tmp(a); // now we have two copies of a
a = b; // now we have two copies of b
b = tmp; // now we have two copies of tmp (aka a)
}
If T is a type for which it can be expensive to copy elements, such as string and vector, swap becomes an expensive
operation (for the standard library, we have specializations of string and vector swap() to deal with that).
Note something curious: We didn't want any copies at all. We just wanted to move the values of a, b,
and tmp around a bit.
In C++0x, we can define "move constructors" and "move assignments" to move rather than copy their argument:
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 lvalueThe idea behind a move assignment is that instead of making a copy, it simply takes the representation from its source and replaces it with a cheap default. For example, for strings s1=s2 using the move assignment would not make a copy of s2's characters; instead, it would just let s1 treat those characters as its own and somehow delete s1's old characters (maybe by leaving them in s2, which presumably are just about to be destroyed).
How do we know whether it's ok to simply move from a source? We tell the compiler:
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
}
move(x) means "you can treat x as an rvalue".
Maybe it would have been better if move() had been called rval(),
but by now move() has been used for years.
The move() template function can be written in C++0x (see the "brief introduction") and and uses rvalue references.
Rvalue references can also be used to provide perfect forwarding.
In the C++0x standard library, all containers are provided with move constructors and move assignment and operations that insert new elements, such as insert() and push_back() have versions that take rvalue references. The net result is that the standard containers and algorithms quietly - without user intervention - improve in performance because they copy less.
See also
union U {
int m1;
complex<double> m2; // error (silly): complex has constructor
string m3; // error (not silly): string has a serious invariant
// maintained by ctor, copy, and dtor
};
In particular
U u; // which constructor, if any? u.m1 = 1; // assign to int member string s = u.m3; // disaster: read from string memberObviously, it's illegal to write one member and then read another but people do that nevertheless (usually by mistake).
C++0x modifies the restrictions of unions to make more member types feasible; in particular, it allows a member of types with constructors and destructors. It also adds a restriction to makes the more flexible unions less error-prone by encouraging the building of discriminated unions.
Union member types are restricted:
union U1 {
int m1;
complex<double> m2; // ok
};
union U2 {
int m1;
string m3; // ok
};
This may look error-prone, but the new restriction helps.
In particular:
U1 u; // ok
u.m2 = {1,2}; // ok: assign to the complex member
U2 u2; // error: the string destructor caused the U destructor to be deleted
U2 u3 = u2; // error: the string = caused the U = to be deleted
Basically, U2 is useless unless you embed it in a struct that keeps track of which member (variant) is used.
So, build discriminate unions, such as:
class Widget { // Three alternative implementations represented as a union
private:
enum class Tag { point, number, text } type; // discriminant
union { // representation
point p; // point has constructor
int i;
string s; // string has default constructor, copy operations, and destructor
};
// ...
widget& operator=(const widget& w) // necessary because of the string variant
{
if (type==Tag::text && w.type==Tag::text) {
s = w.s; // usual string assignment
return *this;
}
if (type==Tag::text) s.~string(); // destroy (explicitly!)
switch (type=w.type) {
case Tag::point: p = w.p; break; // normal copy
case Tag::number: i = w.i; break;
case Tag::text: new(&s;)(w.s); break; // placement new
}
return *this;
}
};
See also:
struct S { int a; }; // S is a POD
struct SS { int a; SS(int aa) : a(aa) { } }; // SS is not a POD
struct SSS { virtual void f(); /* ... */ };
C++0x S and SS are "standard layout types" (a.k.a. POD) because there is really nothing
"magic" about SS: the constructor does not affect the layout (so memcpy() would be fine),
only the initialization rules (memset() would be bad - not enforcing the invariant).
However, SSS will still have an embedded vptr and will not be anything like "plain old data."
C++0x defines POD, trivially copyable types, trivial types, and standard-layout types to deal
with various technical aspects of what used to be PODs.
POD is defined recursively
See also:
string s = "\\w\\\\w"; // I hope I got that rightBasically, a "raw string literal" is a string literal where a backslash is just a backslash so that our example becomes:
string s = R"[\w\\w]"; // I'm pretty sure I got that rightThe original proposal for raw strings presents this as a motivating example
"('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|" // Are the five backslashes correct or not?
// Even experts become easily confused.
The R"[...]" notation is a bit more verbose than the "plain" "..." but "something more" is necessary when you
don't have an escape character: How do you put a quote in a raw string? Easy, unless it is preceded by a ]:
R"["quoted string"]" // the string is "quoted string"So, how do we get the character sequence ]" into a raw string? Fortunately, that's a rare problem, but "[...]" is only the default delimiter pair. We can add delimiters before and after the [...] in "[...]". For example
R"***["quoted string containing the usual terminator ("])"]***" // the string is "quoted string containing the usual terminator ("])"
The character sequence after ] must be identical to the sequence before the [.
This way we can cope with (almost) arbitrarily complicated patterns.
See
123 // int 1.2 // double 1.2F // float 'a' // char 1ULL // unsigned long long 0xD0 // hexadecimal unsigned "as" // stringHowever, in C++98 there are no literals for user-defined types. This can be a bother and also seen as a violation of the principle that user-defined types should be supported as well as built-in types are. In particular, people have requested:
"Hi!"s // string, not ``zero-terminated array of char'' 1.2i // imaginary 123.4567891234df // decimal floating point (IBM) 101010111000101b // binary 123s // seconds 123.56km // not miles! (units) 1234567890123456789012345678901234567890x // extended-precisionC++0x supports ``user-defined literals'' through the notion of literal operators that map literals with a given suffix into a desired type. For example:
constexpr complex<double> operator "" i(long double d) // imaginary literal
{
return {0,d}; // complex is a literal type
}
std::string operator""s (const char* p, size_t n) // std::string literal
{
return string(p,n); // requires free store allocation
}
Note the use of constexpr to enable compile-time evaluation.
Given those, we can write
template<class T> void f(const T&);
f("Hello"); // pass pointer to char*
f("Hello"s); // pass (5-character) string object
f("Hello\n"s); // pass (6-character) string object
auto z = 2+1i; // complex(2,1)
The basic (implementation) idea is that after parsing what could be a literal, the compiler always check for a suffix.
The user-defined literal mechanism simple allows the user to specify a new suffix and what is to be done with the
literal before it. It is not possible to redefine the meaning of a built-in literal suffix or augment the syntax of literals.
A literal operator can request to get its (preceding) literal passed ``cooked'' (with the value it would have had if the
new suffix hadn't been defined) or ``raw'' (the string of characters exactly as typed.
To get an ``uncooked'' string, simply request a const char* argument:
Bignum operator"" x(const char* p)
{
return Bignum(p);
}
f(Bignum);
f(1234567890123456789012345678901234567890x);
Here the C-style string "1234567890123456789012345678901234567890" is passed to operator"" x().
Note that we did not have to explicitly put those digits into a string, though we could:
f("1234567890123456789012345678901234567890"x);
Literal operators that takes strings distinguish between cooked and uncooked by a length argument:
string operator"" s(const char* p, size_t n); // calculate length
string operator"" S(const char* p); // preserve escapes, etc.
"one\ttwo\n"s; // cooked: operator"" s({'o', 'n', 'e', '\t', 't', 'w', 'o', '\n', 0} , 8);
"one\ttwo\n"S; // uncooked: operator"" S({'o', 'n', 'e', '\', 't', 't', 'w', 'o', '\', 'n', 0});
Suffixes will tend to be short (e.g. s for string, i for imaginary, m for meter, and x for extended), so different uses could easily clash. Use namespaces to prevent clashes:
namespace Numerics {
// ...
class Bignum { /* ... */ };
namespace literals {
operator"" X(char const*);
}
}
using namespace Numerics::literals;
See also:
void f [[ noreturn ]] () // f() will never return
{
throw "error"; // OK
}
unsigned char c [[ align(double) ]] [sizeof(double)]; // array of characters, suitably aligned for a double
As you can see, an attribute is placed within double square brackets: [[ ... ]].
noreturn and align are two of the four attributes defined in the standard;
the other two are:
struct B {
virtual void f [[ final ]] (); // do not try to override
};
struct D : B {
void f(); // error
};
struct foo* f [[carries_dependency]] (int i); // hint to optimizer
int* g(int* x, int* y [[carries_dependency]]);
There is a reasonable fear that attributes will be used to create language dialects.
The recommendation is to use attributes to only control things that do not affect the
meaning of a program but might help detect errors (e.g. [[final]]) or help
optimizers (e.g. [[carries_dependency]]).
One planned use for attributes is improved support for OpenMP. For example:
for [[omp::parallel()]] (int i=0; i<v.size(); ++i) {
// ...
}
As shown, attributes can be qualified.
See also:
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 }
The argument [&](int a, int b) { return abs(a)<abs(b); } is a "lambda" (or "lambda function" or "lambda expression"),
which specifies an operation that given two integer arguments a and b returns the result of comparing their
absolute values.
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; });
// ...
}
Some consider this "really neat!"; others see it as a way to write dangerously obscure code.
IMO, both are right.
The [&] is a "capture list" specifying that local names used will be passed by reference. We could have said that we wanted to "cature" only v, we could have said so: [&v;]. Had we wanted to pass v by value, we could have said so: [=v]. Capture nothing is [], capture all by references is [&], and capture all by value is [=].
If an action is neither common nor simple, I recommend using a named function object or function. For example, the example above could have been written:
void f(vectorFor a tiny function, such as this Record name field comparison, the function object notation is verbose, though the generated code is likely to be indentical. In C++98, such function objects had to be non-local to be used as template argument; in C++ this is no longer necessary.& v) { vector indices(v.size()); int count = 0; fill(indices.begin(),indices.end(),[&](){ return ++count; }); struct Cmp_names { const vector<Record>& vr; Comp_names(const vector<Record>& r) :vr(r) { } bool operator()(Record& a, Record& b) const { return vr[a]<vr[b]; } }; // sort indices in the order determined by the name field of the records: std::sort(indices.begin(), indices.end(), Cmp_names(v)); // ... }
To specify a lambda you must provide
void f(vector<X>& v)
{
struct Less {
bool operator<(const X& a, const X& b) { return a.v<b.v; }
};
sort(v.begin(), v.end(), Less()); // C++98: error: Less is local
// C++0x: ok
}
In C++0x, we also have the alternative of using a
lambda expression:
void f(vector<X>& v)
{
sort(v.begin(), v.end(),
[] (const X& a, const X& b) { return a.v<b.v; }); // C++0x
}
It is worth remembering that naming action can be quite useful for documentation and an encouragement to
good design. Also, non-local (necessarily named) entities can be reused.
C++0x also allows values of unnamed types to be used as template arguments:
template<typename T> void foo(T const& t){}
enum X { x };
enum { y };
int main()
{
foo(x); // C++98: ok; C++0x: ok
foo(y); // C++98: error; C++0x: ok
enum Z { z };
foo(z); // C++98: error; C++0x: ok
}
See also:
#define report(test, ...) ((test)?puts(#test):printf(_ _VA_ARGS_ _))
See:
See
try {
throw e;
} catch(...) {
return current_exception();
}
#include "MyVector.h"
extern template class MyVector<int>; // Suppresses implicit instantiation below --
// MyVector<int> will be explicitly instantiated elsewhere
void foo(MyVector<int>& v)
{
// use the vector in here
}
The ``elsewhere'' might look something like this:
#include "MyVector.h" template class MyVector<int>; // Make MyVectorThis is basically a way of avoiding significant redundant work by the compiler and linker.available to clients (e.g., of the shared library
See
// file V99.h:
inline namespace V99 {
void f(int); // does something better than the V98 version
void f(double); // new feature
// ...
}
// file V98.h:
namespace V98 {
void f(int); // does something
// ...
}
// file Mine.h:
namespace Mine {
#include "V99.h"
#include "V98.h"
}
We here have a namespace Mine with both the latest release (V99) and the previous one (V98).
If you want to be specific, you can:
#include "Mine.h" using namespace Mine; // ... V98::f(1); // old version V99::f(1); // new version f(1); // default versionThe point is that the inline specifier makes the declarations from the nested namespace appear exactly as if they had been declared in the enclosing namespace.
This is a very ``static'' and implementer-oriented facility in that the inline specifier has to be placed by the designer of the namespaces -- thus making the choice for all users. It is not possible for a user of Mine to say ``I want the default to be V98 rather than V99.
See
struct S { S(int); }; // "ordinary constructor" defines implicit conversion
S s1(1); // ok
S s2 = 1; // ok
void f(S);
f(1); // ok (but that's often a bad surprise -- what if S was vector?)
struct E { explicit E(int); }; // explicit constructor
E e1(1); // ok
E e2 = 1; // error (but that's often a surprise)
void f(E);
f(1); // error (protects against surprises -- e.g. std::vector's constructor from int is explicit)
However, a constructor is not the only mechanism for defining a conversion.
If we can't modify a class, we can define a conversion operator from a different class. For example:
struct S { S(int) { } /* ... */ };
struct SS {
int m;
SS(int x) :m(x) { }
operator S() { return S(m); } // because S don't have S(SS); non-intrusive
};
SS ss(1);
S s1 = ss; // ok; like an implicit constructor
S s2(ss); // ok ; like an implicit constructor
void f(S);
f(ss); // ok; like an implicit constructor
Unfortunately, there is no explicit conversion operators (because there are far fewer problematic examples).
C++0x deals with that oversight by allowing conversion operators to be explicit. For example:
struct S { S(int) { } };
struct SS {
int m;
SS(int x) :m(x) { }
explicit operator S() { return S(m); } // because S don't have S(SS)
};
SS ss(1);
S s1 = ss; // error; like an explicit constructor
S s2(ss); // ok ; like an explicit constructor
void f(S);
f(ss); // error; like an explicit constructor
See also:
bool all_of(Iter first, Iter last, Pred pred); bool any_of(Iter first, Iter last, Pred pred); bool none_of(Iter first, Iter last, Pred pred); Iter find_if_not(Iter first, Iter last, Pred pred); OutIter copy_if(InIter first, InIter last, OutIter result, Pred pred); OutIter copy_n(InIter first, InIter::difference_type n, OutIter result); OutIter move(InIter first, InIter last, OutIter result); OutIter move_backward(InIter first, InIter last, OutIter result); pair<OutIter1, OutIter2> partition_copy(InIter first, InIter last, OutIter1 out_true, OutIter2 out_false, Pred pred); Iter partition_point(Iter first, Iter last, Pred pred); RAIter partial_sort_copy(InIter first, InIter last, RAIter result_first, RAIter result_last); RAIter partial_sort_copy(InIter first, InIter last, RAIter result_first, RAIter result_last, Compare comp); bool is_sorted(Iter first, Iter last); bool is_sorted(Iter first, Iter last, Compare comp); Iter is_sorted_until(Iter first, Iter last); Iter is_sorted_until(Iter first, Iter last, Compare comp); bool is_heap(Iter first, Iter last); bool is_heap(Iter first, Iter last, Compare comp); Iter is_heap_until(Iter first, Iter last); Iter is_heap_until(Iter first, Iter last, Compare comp); T min(initializer_list<T> t); T min(initializer_list<T> t, Compare comp); T max(initializer_list<T> t); T max(initializer_list<T> t, Compare comp); pair<const T&, const T&> minmax(const T& a, const T& b); pair<const T&, const T&> minmax(const T& a, const T& b, Compare comp); pair<const T&, const T&> minmax(initializer_list<T> t); pair<const T&, const T&> minmax(initializer_list<T> t, Compare comp); pair<Iter, Iter> minmax_element(Iter first, Iter last); pair<Iter, Iter> minmax_element(Iter first, Iter last, Compare comp); void iota(Iter first, Iter last, T value); // For each element referred to by the iterator i in the range [first,last), assigns *i = value and increments value as if by ++value
Consider also that the use of moves allows simple and efficient sort (and other algorithms) of containers of ``smart'' pointers, especially unique_ptr:
template<class P> struct Cmp<P> { // compare *P values
bool operator() (P a, P b) const { return *a<*b; }
}
vector<std::unique_ptr<Big>,Cmp<Big>()> vb;
// fill vb with unique_ptr's to Big objects
sort(vb.begin(),vb.end()); // don't try that with an auto_ptr
vector<unique_ptr<Big>,[](unique_ptr<Big> a, unique_ptr<Big> b) { return *a<*b; });
I expect lambdas to be a bit overused initially (like all powerful mechanisms).
auto x = max({x,y,z},Nocase());
vectorvs = { "Hello", ", ", "World!", "\n" }; for (auto s : vs ) cout << s;
vector<int> make_random(int n)
{
vector<int> ref(n);
for(auto x& : ref) x = rand_int(); // some random number generator
return ref;
}
vector<int> v = make_random(10000);
for (auto x : make_random(1000000)) cout << x << '\n';
The point here is that no vectors are copied.
Rewrite this to return a free-store-allocated vector and you have to deal with memory management.
Rewrite this to pass the vector to be filled as an argument to make_random() and you
have a far less obvious code (plus an addded opportunity for making an error).
vector<pair<string,int>> vp;
string s;
int i;
while(cin>>s>>i) vp.push_back({s,i});
This will construct a pair<string,int> out of r and i and move it into vp.
Note ``move'' not ``copy;''
There is a push_back version that takes an
rvalue reference
argument so that we can take advantage of
string's move constructor.
Note also the use of the
unified initializer syntax to avoid verbosity.
vector<pair<string,int>> vp; string s; int i; while(cin>>s>>i) vp.emplace_back(s,i);An emplace takes a variadic template argument and uses that to construct an object of the desired type. Whether the emplace_back() really is more efficient than the push_back() depends on the types involved and the implementation (of the library and of variadic templates). If you think it matters, measure. Otherwise, choose based on aestetics: vp.push_back({s,i}); or vp.emplace_back(s,i);. For now, I prefer the push_back() version, but that might change over time.
template<class T> Simple_allocator { // C++98 style
// no data
// usual allocator stuff
};
class Arena {
void* p;
int s;
public:
Arena(void* pp, int ss);
// allocate from p[0..ss-1]
};
template<class T> struct My_alloc {
Arena& a;
My_alloc(Arena& aa) : a(aa) { }
// usual allocator stuff
};
Arena my_arena1(new char[100000],100000);
Arena my_arena2(new char[1000000],1000000);
vector<int> v0; // allocate using default allocator
vector<int,My_alloc<int>> v1(My_alloc<int>{my_arena1}); // allocate from my_arena1
vector<int,My_alloc<int>> v2(My_alloc<int>{my_arena2}); // allocate from my_arena2
vector<int,Simple_alloc<int>> v3; // allocate using Simple_alloc
Typically, the verbosity would be alliviated by the use of typedefs.
It is not guaranteed that the default allocator and Simple_alloc takes up no space in a vector object, but a bit of elegant template metaprogramming in the library implementation can ensure that. So, an allocator carries a space cost only if it's objects actually has state (like My_alloc).
A rather sneaky problem can occur when using containers and user-defined allocators:
Should an element be in the same allocation area as its container?
For example, if you use Your_allocator for Your_string to allocate its elements
and I use My_allocator to allocate elements of My_vector
then which allocator should be used for string elements in My_vector
So, we have four alternatives:
See also:
See also:
See also:
The basic idea is simply to use unordered_map as an optimized version of map where
optimization is possible and reasonable. For example:
More to come.
See also:
The element types of a tuple can explicitly specified or be deduced (using get_tuple()
and the elements can be access by (zero-based) index using get():
The most frequently useful tuple is the 2-tuple; that is, a pair.
However, pair is directly supported in the standard library through std::pair (20.3.3 Pairs).
A pair can be used to initialize a tuple, but the opposite isn't the case.
The comparison operatorns (==, !=, <, <=, >, and >=) are defined for tuples of comparable element types.
See also:
It is not possible to just bind arguments for an overloaded function,
we have to explicitly state which version of an overloaded function we want to bind:
function is a type that can hold a value of just about anything you can invoke using the (...) syntax.
In particular, the result of bind can be assigned to a function.
function is very simple to use. For example:
See also:
Here is a conventional piece of exception unsafe code:
One of the uses of unique_ptr is as a pointer in a container, where we might have used a built-in pointer
except for exception safety problems (and to guarantee destruction of the pointed to elements):
unique_ptr is represented by a simple built-in pointer and the overhead of using one compared to a built-in pointer
are miniscule.
In particular, unique_ptr does not offer any form of dynamic checking.
See also
Note that you should not use a shared_ptr just to pass a pointer from one owner to another; that's
what unique_ptr is for and unique_ptr does that cheaper and better.
If you have been using counted pointers as return values from factory functions and the like, consider
upgrading to unique_ptr rather than shared_ptr.
Please don't thoughtlessly replace pointers with shared_ptrs in an attempt to prevent memory leaks;
shared_ptrs are not a panecea nor are they without costs:
See also
Obviously, I radically simplified "the owner" and gave each new Asteroid just one neighbor.
The key is that we give the Asteroid a weak_ptr to that neighbor.
The owner keeps a shared_ptr to represent the ownership that's shared whenever an Asteroid
is looking (but not otherwise).
The collision calculation for an Asteroid will look something like this:
I expect to find weak_ptr use much rarer than "plain" shared_ptr use
and I hope than unique_ptr will become much more popular than shared_ptr use
because unique_ptr represents a simpler (and more efficient) notion of ownership and (therefore) allows better local reasoning.
See also
The rules for pointers and lifetimes are expressed in terms of "safely derived pointer" (3.7.4.3);
roughly: "pointer to something allocated by new or to a sub-object thereof."
Here are some examples of "not safely derived pointers" aka "disguised pointers"
aka what not to do in a program you want to be considered well behaved and
comprehensible to ordinary mortals:
A programmer can specify where there are no pointers to be found (e.g. in an image)
and what memory can't be reclaimed even if the collector can't find a pointer into it:
See also
The key guarantee is: Two threads of execution can update and access separate memory locations
without interfering with each other.
But what is a ``memory location?''
A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having
non-zero width.
For example, here S has exactly four separate memory locations:
So, C++0x guarantees that no such ``words shearing'' problems occur for ``separate memory locations.''
Note that different bitfields within a single word are not separate memory locations,
so don't share structs with bitfields among threads without some form of locking.
Apart from that caveat, the C++ memory model is simply ``as everyone would expect.''
Fortunately, we have already adapted to modern times and every current C++ compiler (that I know of)
gives the one right answer and have done so for years.
After all, C++ has been used for serious systems programming of concurrent systems ``forever.''
See also
Many thick books and tens of thousands of papers have been writing about concurrency, parallelism, and threading,
this FAQ entry barely scratch the surface.
It is hard to think clearly about concurrency.
If you want to do concurrent programming, at least read a book.
Do not rely just on a manual, a standard, or an FAQ.
A thread is launched by constructing a std::thread with a function or a function object (incl. a
lambda):
Typically, we'd like to pass some arguments to the task to be executed (I call something executed by a thread a task).
For example:
In general, we'd also like to get a result back from an executed task.
With plain tasks, there is no notion of a return value; I recommend
std::future for that.
Alternative, we can pass an argument to a task telling it where to put its result:
For example:
When a thread goes out of scope the program is terminate()d unless its task has completed.
That's obviously to be avoided.
There is no way to request a thread to terminate (i.e. request that it exit as a soon as possible and as gracefully as possible) or to
force a thread to terminate (i.e. kill it).
We are left with the options of
The basic problem with threads is data races;
that is, two threads running in a single address space can independendly access an object
in ways that cause undefined results.
If one (or both) writes to the object and the other (or both) reads the object they have a
``race'' for who gets its operation(s) done first.
The results are not just undefined; they are usually completely unpredicatable.
Consiquently, C++0x provides some rules/guarantees for the programmer to avoid data races:
You can
See also
In addition to lock(), a mutex has a try_lock() operation which can be used to try to get into
the critical region without the risk of getting blocked:
A recursive_mutex is a mutex that can be acquired more than once by a thread:
What if I need to acquire a mutex within the next ten seconds?
The timed_mutex class is offered for that.
Its operations are specialized versions of try_lock() with an associated time limit:
There is of course also a recursive_timed_mutex.
A mutex is considered a resource (as it is typically used to represent a real resource)
and must be visible to at least two threads to be useful.
Consequently, it cannot be copied or moved (you couldn't just make another copy of a hardware input register).
It can be surprisingly difficult to get the lock()s and unlock()s to match.
Think of complicated control structures, errors, and exceptions.
If you have a choice, use locks to manage your mutexes;
that will save you and your users a lot of sleep.
See also
This straightforward picture of a lock is clouded by unique_lock having facilities to do just about
everything a mutex can, but safer.
For example, we can use a lock to do try lock:
What if we need two resources represented bt two mutexes?
The naive way is to acquire the mutexes in order:
If you prefer to use try_lock()s yourself, there is an equivalent to lock() to help:
See also
Sorry, I have not had time to write this entry. Please come back later.
See also
See also
See also
See also
See also
See also
See also
Concepts also help template implementers. Consider:
Being able to classify and distinguish different types of types, we can overload based on the kind of types passed.
For example
You can define your own concepts, but for starters the standard library provides a variety of useful concepts,
such as ForwardIterator, Callable, LessThanComparable, and Regular.
Note: the C++0x standard libraries are specified using concepts.
See also
An int* is a ForwardIterator;
we said so when presenting concepts,
the standard has always said so,
and even the first version of the STL used pointers as iterators.
However, we also talked about ForwardIterator's value_type.
But an int* does not have a member called value_type; in fact, it has no members.
So how can an int* be a ForwardIterator?
It is because we say it is.
Using a concept_map, we say that when a T* is used where a ForwardIterator is required,
we consider the T its value_type:
An axiom lists pairs of computations that may be considered equivalent.
Consider:
An axiom is a sequence of equivalence statements (using <=>) and conditional statements
(of the form "if (something) then we may assume the following equivalence"):
using xstring = basic_string<char, char_traits<char>, My_alloc<char>>; // a string with my allocator
Then I must make a version of vector that accepts those strings,
accepts a My_alloc object,
and passes that object on to the string:
using svec = vector<xstring,scoped_allocator_adaptor<
Finally, we can make an allocator of type My_alloc<xstring>:
svec v(scoped_allocator_adaptor(My_alloc<xstring>{my_arena1}));
Now svec is a vector of strings using My_alloc to allocate memory for strings.
What's new is that the standard library ``adaptor'' (''wrapper type'') scoped_allocator_adaptor is used
to indicate that string also should use My_alloc.
Note that the adaptor can (trivially) convert My_alloc<xstring> to the My_alloc<char> that xstring needs.
// vector and string use their own (the default) allocator:
using svec0 = vector<string>;
svec0 v0;
// vector (only) uses My_alloc and string uses its own (the default) allocator:
using svec1 = vector<string,My_alloc<string>>;
svec1 v1(My_alloc<string>{my_arena1});
// vector and string use My_alloc (as above):
using xstring = basic_string<char, char_traits<char>, My_alloc<char>>;
using svec2 = vector<xstring,scoped_allocator_adaptor<My_alloc<xstring>>>;
svec v2(scoped_allocator_adaptor<My_alloc<xstring>>{my_arena1}));
// vector uses My_alloc and string uses My_string_alloc:
using xstring2 = basic_string<char, char_traits<char>, My_string_alloc<char>>;
using svec3 = vector<xstring2,scoped_allocator_adaptor2<My_alloc
Obviously, the first variant, svec0, will be by far the most common, but for systems with serious memory-related
performance constraints, the other versions (especially svec2) can be important.
A few typedefs would make that code a bit more readable, but it is good it is not something you have to
write every day.
The scoped_allocator_adaptor2 is a variant of scoped_allocator_adaptor for the case where
the two non-default allocators differ.
std::array
The standard container array is a fixed-sized random-access sequence of elements defined in <array>.
It has no space overheads beyond what it needs to hold its elements,
it does not use free store,
it can be initialized with an initializer list,
it knows it size (number of elements),
and doesn't convert to a pointer unless you explicitly ask it to.
In other words, it is very much like a built-in array without the problems.
array<int,6> a = { 1, 2, 3 };
a[3]=4;
int x = a[5]; // x becomes 0 because default elements are zero initialized
int* p1 = a; // error: std::array doesn't implicitly convert to a pointer
int* p2 = a.data(); // ok: get pointer to first element
Note that you can have zero-length arrays but that you cannot deduce the length of an array
from an initializer list:
array<int> a3 = { 1, 2, 3 }; // error: size unknown/missing
array<int,0> a0; // ok: no elements
int* p = a0.data(); // unspecified; don't try it
The standard array's features makes it attractive for embedded systems programming
(and similar constrained, preformance-critical, or safety critical tasks).
It is a sequence container so it provides the usual member types and functions (just like vector):
template<class C> C::value_type sum(const C& a)
{
return accumulate(a.begin(),a.end(),0);
}
array<int,10> a10;
array<double,1000> a1000;
vector<int> v;
// ...
int x1 = sum(a10);
int x2 = sum(a1000);
int x3 = sum(v);
Also, you don't get (potentially nasty) derived to base conversions:
struct Apple : Fruit { /* ... */ };
struct Pear : Fruit { /* ... */ };
void nasty(array<Fruit*,10>& f)
{
f[7] = new Pear();
};
array<Apple*,10> apples;
// ...
nasty(apples); // error: can't convert array<Apple*,10> to array<Fruit*,10>;
If that was allowed, apples would now contain a Pear.
std::forward_list
The standard container forward_list, defined in <forward_list>, is basically a singly-linked list.
It supports forward iteration (only) and guarantees that elements don't move if you insert or erase one.
It occupies minimal space (an empty list is likely to be one word) and does not provide a size() operation
(so that it does not have to store a size member):
template <ValueType T, Allocator Alloc = allocator<T> >
requires NothrowDestructible<T>
class forward_list {
public:
// the usual container stuff
// no size()
// no reverse iteration
// no back() or push_back()
};
Unordered containers
A unordered container is a kind of hash table. C++0x offers four standard ones:
They should have been called hash_map etc., but there are so many imcompatible uses of those names that
the committee had to choose new names and the unordered_map, etc. were the least bad we could find.
The "unordered" refers to one of the key differences between map and unordered_map:
When you iterate over a map
you do so in the order provided by its less-than comparison operator (by default <) whereas the value type
of unordered_map is not required to have a less-than comparison operator and a hash table doesn't naturally
provide an order.
Conversely, the element type of a map is not required to have a hash function.
map
The iterator over m will present the elements in alphabetical order; the iteration over um
will not (except through a freak accident).
Lookup is implemented very differently for m and um.
For m lookup involves log2(m.size()) less-than comparisons whereas for um
lookup involves a single call of a hash function and one or more equality operations.
For a few elements (say a few dozen), it is hard to tell which is faster.
For larger numbers of elements (e.g. thousands), lookup in an unordered_map
can be much faster than for a map.
std::tuple
The standard library tuple (an N-tuple) is a ordered sequence of N values where
N can be a constant from 0 to a large implementation-defined value, defined in <tuple>.
You can think of an tuple as an unnamed struct with members of the specified tuple element types.
In particular, the elements of a tuple is stored compactly; a tuple is not a linked structure.
tuple<string,int> t2("Kylling",123);
auto t = make_tuple(string("Herring"),10, 1.23); // t will be of type tuple<string,int,double>
string s = get<0>(t);
int x = get<1>(t);
double d = get<2>(t);
Tuples are used (directly of inderectly) whenever we want a heterogeneous list of elements at compile time
but do not want to define a named class to hold them.
For example, tuple is used internally in std::function and std::bind to
hold arguments.
metaprogramming and type traits
Sorry. Come back later.
std::function and std::bind
The bind and function standard function objects are defined in <functional>
(together with a lot of other function objects);
they are used to handle functions and function arguments.
bind is used to take a function (or a function object or anything you can invoke using the (...) syntax)
and produce a function object with one or more of the arguments of the argument function ``bound'' or rearranged.
For example:
int f(int,char,double);
auto ff = bind(f,_1,'c',1.2); // deduce return type
int x = ff(7); // f(7,'c',1.2);
This binding of arguments is usually called ``Currying.''
The _1 is a place-holder object indicating where the first argument of ff is to go when f is
called through ff.
The first argument is called _1, the second _2, and so on. For example:
int f(int,char,double);
auto frev = bind(f,_3,_2,_1); // reverse argument order
int x = frev(1.2,'c',7); // f(7,'c',1.2);
Note how auto saves us from having to specify the type of the result of bind.
int g(int);
double g(double); // g() is overloaded
auto g1 = bind(g,_1); // error: which g()?
auto g2 = bind((double(*)(double))g,_1); // ok (but ugly)
bind() comes in two variants: the one shown above and a "legacy" version where you explicitly specify the return type:
auto f2 = bind<int>(f,7,'c',_1); // explicit return type
int x = f2(1.2); // f(7,'c',1.2);
This second version was necessary and is widely used becasue the first (and for a user simplest)
version cannot be implemented in C++98.
function<float (int x, int y)> f; // make a function object
struct int_div { // take something you can call using ()
float operator()(int x, int y) const { return ((float)x)/y; };
};
f = int_div(); // assign
cout << f(5, 3) << endl; // call through the function object
std::accumulate(b,e,1,f); // passes beautifully
Member functions can be treated as free functions with an extra argument
struct X {
int foo(int);
};
function<int (X*, int)> f;
f = &X;::foo; // pointer to member
X x;
int v = f(&x;, 5); // call X::foo() for x with 5
function<int (int)> ff = std::bind(f,&x;,_1); // first argument for f is &x;
v=ff(5); // call x.foo(5)
functions are useful for callbacks, for passing operations as argument, etc.
It can be seen as a replacement for the C++98 standard library function objects
mem_fun_t, pointer_to_unary_function, etc.
Similarly, bind() can be seen as a replacement for bind1() and bind2().
unique_ptr
unique_ptr relies critically on rvalue references and move semantics.
X* f()
{
X* p = new X;
// do something - maybe throw an exception
return p;
}
A solution is to hold the pointer to the object on the free store in a unique_ptr:
X* f()
{
unique_ptr<X> p(new X); // or {new X} but not = new X
// do something - maybe throw an exception
return p.release();
}
Now, if an exception is thrown, the unique_ptr will (implicitly) destroy the object pointed to.
That's basic
RAII.
However, unless we really need to return a built-in pointer,
we can do even better by returning a unique_ptr:
unique_ptr<X> f()
{
unique_ptr<X> p(new X); // or {new X} but not = new X
// do something - maybe throw an exception
return p; // the ownership is transferred out of f()
}
We can use this f like this:
void g()
{
unique_ptr<X> q = f(); // move using move constructor
q->memfct(2); // use q
X x = *q; // copy the object pointed to
// ...
} // q and the object it owns is destroyed on exit
The unique_ptr has "move semantics" so the initialization of q with the rvalue that is the result of the call
f() simply transfers ownership into q.
vector<unique_ptr<string>> vs { new string{"Doug"}, new string{"Adams"} };
shared_ptr
A shared_ptr is used to represent shared ownership; that is, when two pieces of code needs access
to some data but neither has exclusive ownership (in the sense of being responsible for destroying the object).
A shared_ptr is a kind of counted pointer where the object pointed to is deleted when the use count goes to zero.
Here is a highly artificial example:
void test()
{
shared_ptr<int> p1(new int); // count is 1
{
shared_ptr<int> p2(p1); // count is 2
{
shared_ptr<int> p2(p1); // count is 3
} // count goes back down to 2
} // count goes back down to 1
} // here the count goes to 0 and the int is deleted.
A more realistic example would be be pointers to nodes in a general graph where someone wanting to remove a pointer
to a node wouldn't know if anyone else held a pointer to that node. If a node can hold resources that
require an action by a destructor (e.g. a file handle so that a file needs to be closed when the node
is deleted).
You could consider shared_ptr to be for what you might consider plugging in a
garbage collector for, except that maybe you don't have enough garbage for that
to be economical, your execution environment doesn't allow that, or the resource managed is not just
memory (e.g. that file handle).
For example:
struct Node { // note: a Node may be pointed to from several other nodes.
shared_ptr<Node> left;
shared_ptr<Node> right;
File_handle f;
// ...
};
Here Node's destructor (the implicitly generated destructor will do fine) deletes its sub-nodes;
that is, left and right's destructors are invoked.
Since left is a shared_ptr,
the Node pointed to (if any) is deleted if left was the last pointer to it; right
is handled similarly and f's destructor does whatever is required for f.
.
A shared_ptr represents
shared ownership but shared ownership isn't my ideal: It is better if an object has a definite
owner and a definite, predictable lifespan.
weak_ptr
Weak pointers are often explained as what you need to break loops in data structures managed using
shared_ptrs.
I think it is better to think of a weak_ptr as a pointer to something that
Consider and implementation of the old "asteroid game".
All asteroids are owned by "the game" but each asteroids must keep track of neighboring asteroids and
handle collisions. A collision typically leads to the destruction of one or more asteroids.
Each asteroid must keep a list of other asteroids in its neighborhood.
Note that being on such a neighbor list should not keep an astroid "alive" (so a shared_ptr
would be inappropriate).
On the other hand, an asteroid must not be destroyed while another asteroid is looking at it
(e.g. to calculate the effect of a collision).
And obviously, an asteroids destructor must be called to release resources (such as a connection to
the graphics system).
What we need is a list of asteroids that might still be intact and a way of "grapping onto one"
for a while. A weak_ptr does just that:
void owner()
{
// ...
vector<shared_ptr<Asteroid>> va(100);
for (int i=0; i<va.size(); ++i) {
// ... calculate neighbors for new asteroid ...
va[i].reset(new Asteroid(weak_ptr<Asteroid>(va[neighbor]));
launch(i);
}
// ...
}
reset() is the function to make a shared_ptr refer to a new object.
void collision(weak_ptr<Asteroid> p)
{
if (auto q = p.lock()) { // p.lock returns a shared_ptr to p's object
// ... that Asteroid still existed: calculate ...
}
else {
// ... oops: that Asteroid has alreadu been destroyed: just forget about it (delete the weak_ptr to it ...
}
}
Note that even if the owner decides to shut down the game and deletes all Asteroids
(by destroying the shared_ptrs representing ownership)
every Asteroid that is in the middle of calculating a collision still finishes correctly
(because after the p.lock() it holds
a shared_ptr that won't just become invalid).
Garbage collection ABI
Garbage collection (automatic recycling of unreferenced regions of memory) is optional in C++;
that is, a garbage collector is not a compulsory part of an implementation.
However, C++0x provides a definition of what a GC can do if one is used and an ABI (Application Binary Interface)
to help control its actions.
There are legitimate reasons to disguise pointers
(e.g. the xor trick in exceptionally memory-constrained applications), but not as many as some programmers think.
int* p = new int;
p+=10;
// ... collector may run here ...
p-=10;
*p = 10; // can we be sure that the int is still there?
int* p = new int;
int x = reinterpret_cast<int>(p); // non-portable
p=0;
// ... collector may run here ...
p = reinterpret_cast<int*>(x);
*p = 10; // can we be sure that the int is still there?
void declare_reachable(void* p); // the region of memory starting at p
// (and allocated by some allocator
// operation which remembers its size)
// must not be collected
template<class T> T* undeclare_reachable(T* p);
void declare_no_pointers(char* p, size_t n); // p[0..n] holds no pointers
void undeclare_no_pointers(char* p, size_t n);
A programmer can inquire which rules for pointer safety and reclamation is in force:
enum class pointer_safety {relaxed, preferred, strict };
pointer_safety get_pointer_safety();
3.7.4.3[4]: If a pointer value that is not a safely-derived pointer value is dereferenced or deallocated,
and the referenced complete object is of dynamic storage duration and has not previously been
declared reachable (20.7.13.7), the behavior is undefined.
There is no standard way of saying which alternative you prefer.
Considered that a "quality of implementation" and a "programming environment" issue.
Memory model
A memory model is an agreement between the machine architects and the compiler writers to ensure that
most programmers do not have to think about the details of modern computer hardware.
Without a memory model, few things realted to threading, locking, and lock-free programming would make sense.
struct S {
char a; // location #1
int b:5, // location #2
c:11,
:0, // note: :0 is "special"
d:8; // location #3
struct {int ee:8;} e; // location #4
};
Why is this important? Why isn't it obvious? Wasn't this always true?
The problem is that when several computations can genuinely run in parallel, that is several
(apparently) unrelated instructions can execute at the same time, the quirks of the memory hardware can get exposed.
In fact, in the absence of compiler support, issues of instruction and data pipelining and
details of cache use will be exposed in ways that are completely unmanageable to
the applications programmer.
This is true even if no two threads have been defined to share data!
Consider, two separately compiled ``threads:''
// thread 1:
char c;
c = 1;
int x = c;
// thread 2:
char b;
b = 1;
int y = b;
For greater realism, I could used the separate compilation (within each thread) to ensure that the compiler/optimizer wouldn't be able to eliminate memory
accesses and simply ignore c and b and directly initialize x and y with 1.
What are the possible values of x and y?
According to C++0x the only correct answer is the obvious one: 1 and 1.
The reason that's interesting is that if you take a conventional good pre-concurrency C or C++ compiler, the possible
answers are 0 and 0, 1 and 0, 0 and 1, and 1 and 1. This has been observed ``in the wild.''
How?
A linker might allocate c and b right next to each other (in the same word) -- nothing in the C or C++ 1990s standards
says otherwise.
In that, C and C++ resembles essentially all languages not designed with real concurrent hardware in mind.
However, most modern processors cannot read or write a single character, it must read or write a whole word,
so the assignment to c really is ``read the word containing c, replace the c part, and write the word back again.''
Since the assignment to b is similar, there are plenty of opportunities for the two threads to clobber each other
even though the threads do not (according to their source texct) share data!
Threads
A thread is a representation of an execution/computation in a program.
In C++0x, as in much modern computing, a thread can -- and usually do -- share an address space with
other threads.
In this, it differs from a process, which generally do not directly share data with other processes.
C++ have had a host of threads implementations for a variety of hardware and operating systems in the past,
what's new is a standard-library threads library, a standard thread ABI.
#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
}
Unfortunately, this is unlikely to give any useful results -- whateven f() and F() might do.
The snag is that the program may terminate before or after t1 executes f() and before or after t2 executes F().
We need to wait for the two tasks to complete:
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
}
The join()s ensure that we don't terminate until the threads have completed.
To ``join'' means to ``wait for the thread to terminate.''
void f(vector<double>&);
struct F {
vector<double>& v;
F(vector<double>& vv) :v{vv} { }
void operator()();
};
int main()
{
std::thread t1{std::bind(f,some_vec)}; // f(some_vec) executes in separate thread
std::thread t2{F(some_vec)}; // F(some_vec)() executes in separate thread
t1.join();
t2.join();
}
Basically, the standard library
bind makes a function object of its arguments.
void f(vector<double>&, double* res); // place result in res
struct F {
vector
But what about errors? What if a task throws an exception?
If a task thows an exception and doesn't catch it itself std::terminate() is called.
That typically means that the program finishes.
We usually try rather hard to avoid that.
A std::future can transmit an exception to the parent/calling thread;
that's one reason I like futures.
Otherwise, return some sort of error code.
This was all the committee could agree upon.
In particular, representatives from POSIX were vermently against any form of ``thread cancellation''
however much C++'s model of resources rely on destructors.
There is no perfect solution for every systems and every possible application.
Concurrent access to a stream object, stream buffer object, or C Library stream
by multiple threads may result in a data race unless otherwise specified.
So don't share an output stream between two threads unless you somehow control the access to it.
Multual exclusion
A mutex is a primitive object use for controlling access in a multi-threaded system.
The most basic use is
std::mutex m;
int sh; // shared data
// ...
m.lock();
// manipulate shared data:
sh+=1;
m.unlock();
Only one thread at a time can be in the region of code between the lock() and the unlock() (often called a critical region).
If a second thread tries m.lock() while a first thread is executing in that region, that second thread
is blocked until the first execures the m.unlock().
This is simple.
What is not simple is to use mutexes in a way that doesn't cause serious problems:
What if a thread ``forgets'' to unlock()?
What if a thread tries to lock() the same mutex twice?
What if a thread waits a very long time before doing an unlock()?
What if a thread need to lock() two mutexes to do its job?
The complete answers fill books.
Here (and in the Locks section) are just the raw basics.
std::mutex m;
int sh; // shared data
// ...
if (m.try_lock()) {
// manipulate shared data:
sh+=1;
m.unlock();
else {
// maybe do something else
}
std::recursive_mutex m;
int sh; // shared data
// ...
void f(int i)
{
// ...
m.lock();
// manipulate shared data:
sh+=1;
if (--i>0) f(i);
m.unlock();
// ...
}
Here, I have been blatant and let f() call itself.
Typically, the code is more subtle.
The recursive call will be indirect along the line of f() calls g() that calls h() that calls f().
std::timed_mutex m;
int sh; // shared data
// ...
if (m.try_lock_for(std::chrono::seconds(10))) {
// manipulate shared data:
sh+=1;
m.unlock();
}
else {
// we didn't get the mutex; do something else
}
The try_lock_for() takes a relative time, a duration as its argument.
If instead you want to wait until a fixed point in time, a time_point
you can use try_lock_until():
std::timed_mutex m;
int sh; // shared data
// ...
if (m.try_lock_until(midnight)) {
// manipulate shared data:
sh+=1;
m.unlock();
}
else {
// we didn't get the mutex; do something else
}
The midnight is a feeble joke: for a mechanism as low level as mutexes, the timescale is more
likely to be milliseconds than hours.
Locks
A lock is an object that can hold a reference to a mutex and may unlock() the mutex
during the lock�s destruction
(such as when leaving block scope).
A thread may use a lock to aid in managing mutex ownership
in an exception safe manner.
In other words, a lock implements
Resource Acquisition Is Initialization
for mutual exclusion.
For example:
std::mutex m;
int sh; // shared data
// ...
void f()
{
// ...
std::unique_lock lck(m);
// manipulate shared data:
sh+=1;
}
A lock can be moved (the purpose of a lock is to represent local ownership of a non-local resource),
but not copied (which copy would own the resource/mutex?).
std::mutex m;
int sh; // shared data
// ...
void f()
{
// ...
std::unique_locklck(m,std::defer_lock); // make a lock, but don't aquire the mutex
// ...
if (lck.try_lock()) {
// manipulate shared data:
sh+=1;
}
else {
// maybe do something else
}
}
Similarly, unique_lock supports try_lock_for() and try_lock_until().
What you get from using a lock rather than the mutex directly is exception handling and protection
against forgetting to unlock().
In concurrent programming, we need all the help we can get.
std::mutex m1;
std::mutex m2;
int sh1; // shared data
int sh2
// ...
void f()
{
// ...
std::unique_lock lck1(m1);
std::unique_lock lck2(m2);
// manipulate shared data:
sh1+=sh2;
}
This has the potentially deadly flaw that some other thread could try to acquire m1 and m2 in the
opposite order so that each had one of the locks needed to proceed and would waith forever for the
second (that's called deadlock).
With many locks in a system, that's a real danger.
Consequently, the standard locks provide two functions for (safely) trying to acquire two or more locks:
void f()
{
// ...
std::unique_lock lck1(m1,std::defer_lock); // make locks but don't yet try to acquire the mutexes
std::unique_lock lck2(m2,std::defer_lock);
std::unique_lock lck3(m3,std::defer_lock);
lock(lck1,lck2,lck3);
// manipulate shared data
}
Obviously, the implementation of lock() has to be carefully crafted to avoid deadlock.
In essense, it will do the equivalent to careful use of try_lock()s.
If lock() fails to acquire all locks it will throw an exception.
Actually, lock() can take any argument with lock(), try_lock(), and unlock()
member functions (e.g. a mutex), so we
can't be specific about which exception lock() might throw; that depends on its arguments.
void f()
{
// ...
std::unique_lock lck1(m1,std::defer_lock); // make locks but don't yet try to acquire the mutexes
std::unique_lock lck2(m2,std::defer_lock);
std::unique_lock lck3(m3,std::defer_lock);
int x;
if ((x = try_lock(lck1,lck2,lck3))==-1) { // welcome to C land
// manipulate shared data
}
else {
// x holds the index of a mutex we could not acquire
// e.g. if lck2.try_lock() failed x==1
}
}
Condition variables
Condition variables provide synchronization primitives used to block a thread until notified by some other
thread that some condition is met or until a system time is reached.
std:duration and std::time_point
Sorry, I have not had time to write this entry. Please come back later.
Atomics
Sorry, I have not had time to write this entry. Please come back later.
std::future and std::promise
Sorry, I have not had time to write this entry. Please come back later.
std::async()
Sorry, I have not had time to write this entry. Please come back later.
abandoning a process
Sorry, I have not had time to write this entry. Please come back later.
Random number generation
Sorry, I have not had time to write this entry. Please come back later.
Regular expressions
Sorry, I have not had time to write this entry. Please come back later.
Concepts
"Concepts" is a mechanism for describing requirements on types, combinations of types, and combinations of types and integers.
It is particularly useful for getting early checking of uses of templates.
Conversely, it also helps early detection of errors in a template body.
Consider the standard library algorithm fill:
template<ForwardIterator Iter, class V> // types of types
requires Assignable<Iter::value_type,V> // relationships among argument types
void fill(Iter first, Iter last, const V& v); // just a declaration, not definition
fill(0, 9, 9.9); // Iter is int; error: int is not a ForwardIterator
// int does not have a prefix *
fill(&v;[0], &v;[9], 9.9); // Iter is int; ok: int* is a ForwardIterator
Note that we only declared fill(); we did not define it (provide its implementation).
On the other hand, we explicitly stated what fill() requires from its argument:
We knew that, of course, having read the standard.
However, compilers do not read requirement documents, so we had to tell it in code
using the concepts ForwardIterator and Assignable.
The result is that errors in the use of fill() are caught immediately at the point of use
and that error messages are greatly improved.
The compiler now has the information about the programmers' intents to allow good checking and good diagnostics.
template<ForwardIterator Iter, class V>
requires Assignable<Iter::value_type,V>
void fill(Iter first, Iter last, const V& v)
{
while (first!=last) {
*first = v;
first=first+1; // error: + not defined for Forward_iterator
// (use ++first)
}
}
This error is caught immediately, eliminating the need for much tedious testing
(though of course not all testing).
// iterator-based standard sort (with concepts):
template<Random_access_iterator Iter>
requires Comparable<Iter::value_type>
void sort(Iter first, Iter last); // use the usual implementation
// container-based sort:
template<Container Cont>
requires Comparable<Cont::value_type>
void sort(Cont& c)
{
sort(c.begin(),c.end()); // simply call the iterator version
}
void f(vector<int>& v)
{
sort(v.begin(), v.end()); // one way
sort(v); // another way
// ...
}
Concept maps
template<Value_type T>
concept_map ForwardIterator<T*> { // T*'s value_type is T
typedef T value_type;
};
A concept_map allows us to say how we want to see a type,
saving us from having to modify it or to wrap it into a new a type.
"Concept maps" is a very flexible and general mechanism for adapting independently developed software for common use.
Axioms
An axiom is a set of predicates specifying the semantics of a concept.
The primary use cases for axioms are external tools (e.g. not the common compiler actions), such as tools for domain-specific
optimizations (languages for specifying program transformations were a significant part of the motivation for axioms).
A secondary use is simply precise specification of semantics in the standard
(as is used in many parts of the standard library specification).
Axioms may also be useful for some optimizations (done by compilers and traditional optimizers), but compilers are
not required to take notice of user-supplied axioms; they work based on the semantics defined by the standard.
concept Semigroup<typename Op, typename T> : CopyConstructible<T> {
T operator()(Op, T, T);
axiom Associativity(Op op, T x, T y, T z) {
op(x, op(y, z)) <=> op(op(x, y), z); // T's operator may be assumed to be associative
}
}
concept Monoid<typename Op, typename T> : Semigroup<Op, T> { // a monoid is a semigroup with an identity element
T identity_element(Op);
axiom Identity(Op op, T x) {
op(x, identity_element(op)) <=> x;
op(identity_element(op), x) <=> x;
}
}
The <=> is the equivalence operator, which is used only in axioms.
Note that you cannot (in general) prove an axiom; we use axioms to state what we cannot prove,
but what a programmer can state to be an acceptable assumption.
Note that both sides of an equivalence statement may be illegal for some values, e.g. use of a NaN (not a number)
for a floating-point type: if both sides of an equivalence uses a NaN both are (obviously) invalid and equivalent
(independently of what the axiom says), but if only one side uses a NaN there may be opportunities for taking
advantage of the axiom.
// in concept TotalOrder:
axiom Transitivity(Op op, T x, T y, T z)
{
if (op(x, y) && op(y, z)) op(x, z) <=> true; // conditional equivalence
}
See also