GCC 4.5 & C++0x

April 15th, 2010

GCC 4.5.0 is out and their progress on implementing C++0x features is coming along nicely.

If you are on OS X and want to try it out you can install it via MacPorts:

sudo port install gcc45

The binary installed is named g++-mp-4.5 and you must use the -std=c++0x argument to enable the new features.

Of the supported C++0x features here are some of those that I find the most interesting (for my use of C++).

Local and Unnamed Types as Template Arguments

The most common scenario in which I need this is when declaring a local lookup structure that I need to iterate. I have my own set of beginof/endof functions overloaded for most types (something that will be redundant with C++0x but which GCC does not yet seem to provide), for example for the array overload I have:

template <typename T, int N> T* beginof (T (&a)[N]) { return a; }
template <typename T, int N> T* endof (T (&a)[N])   { return a + N; }

This allows writing a generic foreach macro like this:

#define foreach(i, c) \
   for(decltype(beginof(c)) i = beginof(c); i != endof(c); ++i)

I am using decltype which is another C++0x feature but prior to this there was the typeof GCC extension.

With the macro we can write code like this:

int xs[] = { 1, 2, 3 };
foreach(x, xs)
    printf("%d\n", *x);

But prior to C++0x we would get an error for this code:

struct { char const* name; int value; } values[] =
    { "foo", 1 },
    { "bar", 2 }

foreach(value, values)
    printf("%s\n", value->name);

The reason for the error is that values is both a local and unnamed type, and it is being passed as an argument to two template functions (beginof/endof).

But with C++0x this is now allowed!

Initializer Lists

Basically std::initializer_list<T> is the type given to “values in braces”. This means “values in braces” is now a type we can work with, e.g. receive as a constructor argument.

Looking at the code above, my local unnamed type was really a map. The reason why I would use a custom struct is mainly because I can declare the values in one go (w/o the overhead of calling functions). But now that “values in braces” has a type, std::map can be initialized from it:

std::map<std::string, int> values =
    { "foo", 1 },
    { "bar", 2 }

Type Inference

If we continue with the example above we may want to search our values map using the find member function. The result of this is an iterator, the type of that is std::map<std::string, int>::[const_]iterator.

Starting with C++0x we can use auto instead, e.g.:

auto foo = values.find("foo");
if(foo != values.end())
    printf("foo’s value is %d\n", foo->second);

Many advocate dynamic typing because they think static typing automatically require manifest typing. With the auto keyword and use of template functions, C++ is moving further and further away from that dreadful paradigm :)

Lambda Functions

This is probably what I am the most excited about but not sure how much I will actually use it.

It is however painful having to define a new function (outside current scope) whenever using a standard library algorithm that takes a function argument, especially since many of the algorithms are effectively just saving me the loop, e.g. std::find_if can be written in two lines with the actual comparison included in those two lines.

Following the style of this post, let me give an example of using std::find_if with a lambda:

it = std::find_if(it, last, [](char ch){ return !isalnum(ch) && ch != '_'; });

Here we advance the iterator (it) to skip alpha numeric characters and underscores.

The lambda can capture one or more variables from the current scope either by value or reference. This is declared inside the square brackets. Use & to capture everything by reference, = to capture everything by value, or provide a list of variables that should be captured (with & as prefix if by reference).

Explicit Conversion

One thing I love about C++ is its ability to do implicit conversions.

For example I can define this type:

struct my_type_t
    my_type_t ()         : initialized(false)          { }
    my_type_t (size_t i) : initialized(true), value(i) { }

    operator bool () const { return initialized; }

    my_type_t operator+ (my_type_t const& rhs) const
        return my_type_t(value + rhs.value);

    bool initialized;
    size_t value;

And then this function:

my_type_t foo (my_type_t const& arg)
    return arg + 8;

Here I rely on implicit construction of my_type_t from 8 but that will actually fail. The reason is that the compiler could also convert arg to bool (as we make use of in the if) and then add together a boolean and integer.

To avoid this problem we prefix the operator bool with explicit and can drop the alternative workaround for this problem.

Slightly related is the ability to delete functions. Say we are very strict about the API usage and we only want the user to construct my_type_t from size_t as opposed to int. The way to enforce this is to add the following constructor signature:

my_type_t (int) = delete;

An alternative to delete is default which gives us the default implementation.

Scoped Enumerations

I often declare enumerations like this:

namespace color { enum type { red, green, blue }; }
color::type c = color::red;

This however is not possible with enumerations declared inside a class (as we can’t nest a namespace inside a class). This menas the enumeration constants are declared in the scope of the class which can cause a problem, e.g.:

class consumer_t
    enum state_t { active, done } state;
    bool done () const; // error: we already declared ‘done’

For this reason I have changed my enumeration convention to:

enum state_t { kActive, kDone };

While this avoids most clashes the constants are still exported into too big a scope. C++0x has a new enum class that avoids this:

class consumer_t
    enum class state_t { active, done } state;
    bool done () const { return state == state_t::done; }

Closing Words

There is still lots of cool stuff to come: range-based for, delegating/inheriting constructors, extensible literals, move semantics, all the stuff about threading, etc.

Unfortunately if you want to develop for Cocoa then you are out of luck, since Apple’s fork of GCC is not going to incorporate these improvements due to them being licensed under the latest version of the GPL.

I have not looked into building for Cocoa with the GCC included with MacPorts. If you have successful experience with that, let me know!

[by Allan Odgaard]

Leave a Reply