The ISO C++ committee met in Sophia Antipolis, France on June 6-14. You can find the minutes here (note that these cover only the whole-group sessions, not the breakout technical sessions where we spend most of the week).

Here’s a summary of what we did, with links to the relevant papers to read for more details, and information about upcoming meetings.

Highlights: Complete C++0x draft coming in September

The biggest goal entering this meeting was to make C++0x feature-complete and stay on track to publish a complete public draft of C++0x this September for international review and comment — in ISO-speak, an official Committee Draft or CD. We are going to achieve that, so the world will know the shape of C++0x in good detail this fall.

We’re also now planning to have two rounds of international comment review instead of just one, to give the world a good look at the standard and two opportunities for national bodies to give their comments, the first round starting after our September 2008 meeting and the second round probably a year later. However, the September 2008 CD is “it”, feature-complete C++0x. The only changes expected to be made between that CD and the final International Standard are bug fixes and clarifications. It’s helpful to think of a CD as a feature-complete beta.

Coming into the June meeting, we already had a nearly-complete C++0x internal working draft — most features that will be part of C++0x had already been “checked in.” Only a few were still waiting to become stable enough to vote in, including initializer lists, range-based for loops, and concepts.

Of these, concepts is the long-pole feature for C++0x, which isn’t surprising given that it’s the biggest new language feature we’re adding to Standard C++. A primary goal of the June meeting, therefore, was to make as much progress on concepts as possible, and to see if it would be possible to vote that feature into the C++0x working draft at this meeting. We almost did that, thanks to a lot of work not only in France but also at smaller meetings throughout the winter and spring: For the first time, we ended a meeting with no known issues or controversial points in the concepts standardese wording, and we expect to “check in” concepts into the working draft at the next meeting in September, which both cleared the way for us to publish a complete draft then and motivated the plan to do two rounds of public review rather than one, just to make sure the standard got enough “bake time” in its complete form.

Next, I’ll summarize some of the major features voted into the draft at the June meeting.

Initializer lists

C++0x initializer lists accomplish two main objectives:

Uniformity: They provide a uniform initialization syntax you can use consistently everywhere, which is especially helpful when you write templates.

Convenience: They provide a general-purpose way of using the C initializer syntax for all sorts of types, notably containers.

See N2215 for more on the motivation and original design, and N2672 and N2679 for the final standardese.

Example: Initializing aggregates vs. classes

It’s convenient that we can initialize aggregates like this:

struct Coordinate1 {

int i;

int j;

//…

}; Coordinate1 c1 = { 1, 2 };

but the syntax is slightly different for classes with constructors:

class Coordinate2 {

public:

Coordinate2( int i, int j );

// …

}; Coordinate2 c2( 1, 2 );

In C++0x, you can still do all of the above, but initializer lists give us a regular way to initialize all kinds of types:

Coordinate1 c1 = { 1, 2 };

Coordinate2 c2 = { 1, 2 } ; // legal in C++0x

Having a uniform initialization syntax is particularly helpful when writing template code, so that the template can easily work with a wide variety of types.

Example: Initializing arrays vs. containers

One place where the lack of uniform initialization has been particularly annoying — at least to me, when I write test harnesses to exercise the code I show in articles and talks — is when initializing a container with some default values. Don’t you hate it when you want to create a container initialized to some known values, and if it were an array you can just write:

string a[] = { “xyzzy”, “plugh”, “abracadabra” };

but if it’s a container like a vector, you have to default-construct the container and then push every entry onto it individually:

// Initialize by hand today

vector<string> v;

v.push_back( “xyzzy” );

v.push_back( “plugh” );

v.push_back( “abracadabra” );

or, even more embarrassingly, initialize an array first for convenience and then construct the vector as a copy of the array, using the vector constructor that takes a range as an iterator pair:

// Initialize via an array today

string a[] = { “xyzzy”, “plugh”, “abracadabra” }; // put it into an array first

vector<string> v( a, a+3 ); // then construct the vector as a copy

Arrays are weaker than containers in nearly every other way, so it’s annoying that they get this unique convenience just because of their having been built into the language since the early days of C.

The lack of convenient initialization has been even more irritating with maps:

// Initialize by hand today

map<string,string> phonebook;

phonebook[ “Bjarne Stroustrup (cell)” ] = “+1 (212) 555-1212”;

phonebook[ “Tom Petty (home)” ] = “+1 (858) 555-9734”;

phonebook[ “Amy Winehouse (agent)” ] = “+44 20 74851424”;

In C++0x, we can initialize any container with known values as conveniently as arrays:

// Can use initializer list in C++0x

vector<string> v = { “xyzzy”, “plugh”, “abracadabra” } ;

map<string,string> phonebook =

{ { “Bjarne Stroustrup (cell)”, “+1 (212) 555-1212” },

{ “Tom Petty (home)”, “+1 (858) 555-9734” },

{ “Amy Winehouse (agent)”, “+44 99 74855424” } };

As a bonus, a uniform initialization syntax even makes arrays easier to deal with. For example, the array initializer syntax didn’t support arrays that are dynamically allocated or class members arrays:

// Initialize dynamically allocated array by hand today

int* a = new int[3];

a[0] = 1;

a[1] = 2;

a[2] = 99;



// Initialize member array by hand today

class X {

int a[3];

public:

X() { a[0] = 1; a[1] = 2; a[2] = 99; }

};

In C++0x, the array initialization syntax is uniformly available in these cases too:

// C++0x int* a = new int[3] { 1, 2, 99 } ; class X {

int a[3];

public:

X() : a { 1, 2, 99 } {}

};

More concurrency support

Last October, we already voted in a state-of-the-art memory model, atomic operations, and a threading package. That covered the major things we wanted to see in this standard, but a few more were still in progress. Here are the major changes we made this time that relate to concurrency.

Thread-local storage (N2659): To declare a variable which will be instantiated once for each thread, use the thread_local storage class. For example:

class MyClass {

…

private:

thread_local X tlsX;

}; void SomeFunc() {

thread_local Y tlsY;

…

};

Dynamic initialization and destruction with concurrency (N2660) handles two major cases:

Static and global variables can be concurrently initialized and destroyed if you try to access them on multiple threads before main() begins. If more than one thread could initialize (or use) the variable concurrency, however, it’s up to you to synchronize access.

Function-local static variables will have their initialization automatically protected; while one thread is initializing the variable, any other threads that enter the function and reach the variable’s declaration will wait for the initialization to complete before they continue on. Therefore you don’t need to guard initialization races or initialization-use races, and if the variable is immutable once constructed then you don’t need to do any synchronization at all to use it safely. If the variable can be written to after construction, you do still need to make sure you synchronize those post-initialization use-use races on the variable.

Thread safety guarantees for the standard library (N2669): The upshot is that the answer to questions like “what do I need to do to use a vector<T> v or a shared_ptr<U> sp thread-safely?” is now explicitly “same as any other object: if you know that an object like v or sp is shared, you must synchronize access to it.” Only a very few objects need to guarantee internal synchronization; the global allocator is one of those, though, and so it gets that stronger guarantee.

The “other features” section below also includes a few smaller concurrency-related items.

More STL algorithms (N2666)

We now have the following new STL algorithms. Some of them fill holes (e.g., “why isn’t there a copy_if or a counted version of algorithm X?”) and others provide handy extensions (e.g., “why isn’t there an all_of or any_of?”).

all_of( first, last, pred )

returns true iff all elements in the range satisfy pred

returns true iff all elements in the range satisfy pred any_of( first, last, pred )

returns true iff any element in the range satisfies pred

returns true iff any element in the range satisfies pred copy_if( first, last result, pred)

the “why isn’t this in the standard?” poster child algorithm

the “why isn’t this in the standard?” poster child algorithm copy_n( first, n result)

copy for a known number of n elements

copy for a known number of n elements find_if_not( first, last, pred )

returns an iterator to the first element that does not satisfy pred

returns an iterator to the first element that does not satisfy pred iota( first, last, value )

for each element in the range, assigns value and increments value “as if by ++value”

for each element in the range, assigns value and increments value “as if by ++value” is_partitioned( first, last, pred )

returns true iff the range is already partitioned by pred; that is, all elements that satisfy pred appear before those that don’t

returns true iff the range is already partitioned by pred; that is, all elements that satisfy pred appear before those that don’t none_of( first, last, pred )

returns true iff none of the elements in the range satisfies pred

returns true iff none of the elements in the range satisfies pred partition_copy( first, last, out_true, out_false, pred )

copy the elements that satisfy pred to out_true, the others to out_false

copy the elements that satisfy pred to out_true, the others to out_false partition_point( first, last, pred)

assuming the range is already partitioned by pred (see is_partitioned above), returns an iterator to the first element that doesn’t satisfy pred

assuming the range is already partitioned by pred (see is_partitioned above), returns an iterator to the first element that doesn’t satisfy pred uninitialized_copy_n( first, n, result )

uninitialized_copy for a known number of n elements

Other approved features

N2435 Explicit bool for smart pointers

N2514 Implicit conversion operators for atomics

N2657 Local and unnamed types as template arguments

N2658 Constness of lambda functions

N2667 Reserved namespaces for POSIX

N2670 Minimal support for garbage collection and reachability-based leak detection

N2661 Time and duration support

N2674 shared_ptr atomic access

N2678 Error handling specification for Chapter 30 (Threads)

N2680 Placement insert for standard containers

Next Meeting

Although we just got back from France, the next meeting of the ISO C++ standards committee is already coming up fast:

The meetings are public, and if you’re in the area please feel free to drop by.