Lambdas, auto, and static_assert: C++0x Features in VC10, Part 1

Lambdas, auto, and static_assert: C++0x Features in VC10, Part 1

Rate This
  • Comments 91

The Visual C++ compiler in the Microsoft Visual Studio 2010 September Community Technology Preview (CTP) contains support for four C++0x language features, namely lambdas, auto, static_assert, and rvalue references.  Today, I'm going to explain the first three features in detail.  (And I'm going to dedicate an entire post to explaining rvalue references in the near future, simply because explaining them now would double the length of this already very long post.)

 

First, a few quick things:

 

1. Today's post is brought to you by Stephan T. Lavavej, Visual C++ Libraries Developer, and the letters C, A, and T.  Note that as a libraries dev, I didn't implement these features.  That was the work of Jonathan Caves, front-end compiler dev, voting Standardization Committee member, and all-around ninja.

 

2. I refer to the Visual C++ compiler in VS 2010 as VC10 (VS 2008 contained VC9, VS 2005 contained VC8, etc. - 10 is not short for 2010).

 

3. C++0x refers to the upcoming C++ Standard, which is still being drafted.  (The Standardization Committee hopes that they'll be finished in 2009, making it C++09; the joke is that if it slips to 2010 or later, the 'x' will be hexadecimal.)  C++98 and C++03 refer to the current C++ Standard.  (Without going into a history lecture here, the 2003 C++ Standard was merely a "service pack" for the original 1998 C++ Standard, and most people can disregard the differences.  C++03 and C++0x are totally different, despite appearances.)

 

4. I'd like to thank the Standardization Committee for developing these wonderfully useful and well-crafted features.  They also make important documents available on their website:

 

C++0x language feature status: http://open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2705.html

C++0x library feature status: http://open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2706.html

C++0x Working Draft: http://open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2798.pdf

 

5. There are always bugs (although hopefully not too many), which is the whole point of the CTP.  Please report bugs to us via Microsoft Connect.

 

Now, let's look at the features!

 

 

lambdas

In C++0x, "lambda expressions" implicitly define and construct unnamed function objects, which then behave like handwritten function objects.  This is the "Hello, World" lambda:

 

C:\Temp>type meow.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 meow.cpp > NUL && meow

0 1 2 3 4 5 6 7 8 9

 

The [] is the lambda-introducer, which tells the compiler that a lambda expression is beginning.  The (int n) is the lambda-parameter-declaration, which tells the compiler what the unnamed function object class's function call operator should take.  Finally, the { cout << n << " "; } is the compound-statement which serves as the body of the unnamed function object class's function call operator.  By default, the unnamed function object class's function call operator returns void.

 

 

So, C++0x has mentally translated this into what you'd write in C++98:

 

C:\Temp>type meow98.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

struct LambdaFunctor {

    void operator()(int n) const {

        cout << n << " ";

    }

};

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    for_each(v.begin(), v.end(), LambdaFunctor());

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 meow98.cpp > NUL && meow98

0 1 2 3 4 5 6 7 8 9

 

Now I'm going to stop saying things like "the unnamed function object class's function call operator returns void" and start saying "the lambda returns void", but it's important to remember what lambda expressions are doing: defining classes and constructing objects.

 

 

Of course, the compound-statement of a lambda can contain multiple statements:

 

C:\Temp>type multimeow.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    for_each(v.begin(), v.end(), [](int n) {

        cout << n;

 

        if (n % 2 == 0) {

            cout << " even ";

        } else {

            cout << " odd ";

        }

    });

 

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 multimeow.cpp > NUL && multimeow

0 even 1 odd 2 even 3 odd 4 even 5 odd 6 even 7 odd 8 even 9 odd

 

 

Now, lambdas don't always have to return void.  If a lambda's compound-statement is { return expression; } , then the lambda's return type will be automatically deduced to be the type of expression:

 

C:\Temp>type cubicmeow.cpp

#include <algorithm>

#include <deque>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    deque<int> d;

 

    transform(v.begin(), v.end(), front_inserter(d), [](int n) { return n * n * n; });

 

    for_each(d.begin(), d.end(), [](int n) { cout << n << " "; });

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 cubicmeow.cpp > NUL && cubicmeow

729 512 343 216 125 64 27 8 1 0

 

Here, the type of n * n * n is int, so this lambda's function call operator returns int.

 

 

Lambdas with more complicated compound-statements don't get automatically deduced return types.  You have to explicitly specify them:

 

C:\Temp>type returnmeow.cpp

#include <algorithm>

#include <deque>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    deque<double> d;

 

    transform(v.begin(), v.end(), front_inserter(d), [](int n) -> double {

        if (n % 2 == 0) {

            return n * n * n;

        } else {

            return n / 2.0;

        }

    });

 

    for_each(d.begin(), d.end(), [](double x) { cout << x << " "; });

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 returnmeow.cpp > NUL && returnmeow

4.5 512 3.5 216 2.5 64 1.5 8 0.5 0

 

The -> double is the optional lambda-return-type-clause.  Why doesn't it go on the left, like what programmers have been doing with C functions for longer than I've been alive?  Because then the lambda-introducer wouldn't come first, and that's what tells the compiler that a lambda expression is beginning.  (Figuring out this kind of stuff is what the Core Working Group excels at; trying to imagine whether a given construct would be parseable within C++ hurts my head.)

 

 

If you forget the lambda-return-type-clause, the compiler will complain about each return statement:

 

C:\Temp>cl /EHsc /nologo /W4 borkedreturnmeow.cpp

borkedreturnmeow.cpp

borkedreturnmeow.cpp(20) : error C3499: a lambda that has been specified to have a void return type cannot return a value

borkedreturnmeow.cpp(22) : error C3499: a lambda that has been specified to have a void return type cannot return a value

 

 

All of the lambdas I've presented so far have been stateless: they contain no data members.  You can have stateful lambdas too, and this is accomplished through "capturing" local variables.  The empty lambda-introducer [] says "I am a stateless lambda".  But within the lambda-introducer, you can specify a capture-list:

 

C:\Temp>type capturekittybyvalue.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 0;

    int y = 0;

 

    // op>>() leaves newlines on the input stream,

    // which can be extremely confusing. I recommend

    // avoiding it, and instead using non-member

    // getline(cin, str) to read whole lines and

    // then parse them. But in the interests of

    // brevity, I'll use evil op>>():

 

    cout << "Input: ";

    cin >> x >> y;

 

    v.erase(remove_if(v.begin(), v.end(), [x, y](int n) { return x < n && n < y; }), v.end());

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 capturekittybyvalue.cpp > NUL && capturekittybyvalue

Input: 4 7

0 1 2 3 4 7 8 9

 

 

If you forget the capture-list, the compiler will complain:

 

C:\Temp>cl /EHsc /nologo /W4 borkedcapturekittybyvalue.cpp

borkedcapturekittybyvalue.cpp

borkedcapturekittybyvalue.cpp(27) : error C3493: 'x' cannot be implicitly captured as no default capture mode has been specified

borkedcapturekittybyvalue.cpp(27) : error C3493: 'y' cannot be implicitly captured as no default capture mode has been specified

 

(I'll explain default captures soon.)

 

Remember, the lambda expression is implicitly defining an unnamed function object class.  The compound-statement { return x < n && n < y; } serves as the body of the function call operator within that class.  Although the compound-statement is lexically within the scope of main(), it is conceptually outside the scope of main(), so you can't use local variables from main() without capturing them within the lambda.

 

 

Here's what this is being mentally translated into:

 

C:\Temp>type capturekittybyvalue98.cpp

#include <algorithm>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

class LambdaFunctor {

public:

    LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }

 

    bool operator()(int n) const { return m_a < n && n < m_b; }

 

private:

    int m_a;

    int m_b;

};

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 0;

    int y = 0;

 

    cout << "Input: ";

    cin >> x >> y; // EVIL!

 

    v.erase(remove_if(v.begin(), v.end(), LambdaFunctor(x, y)), v.end());

 

    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 capturekittybyvalue98.cpp > NUL && capturekittybyvalue98

Input: 4 7

0 1 2 3 4 7 8 9

 

Here, you can clearly see that the captures are "by value".  Copies of the local variables are being stored within the function object.  This allows the function object to outlive the local variables that were captured to create it.  However, note that (a) the captured copies can't be modified within the lambda, because by default the function call operator is const, (b) some objects are expensive to copy, and (c) updates to the local variables will not be reflected in the captured copies (these are ordinary value semantics).  Soon, I'll explain how to deal with each of these when necessary.

 

 

But first, instead of specifying every local variable that you want to capture, you can say "capture everything by value".  The syntax for this is the lambda-introducer [=] (the capture-default = is supposed to make you think of assignment or copy-initialization Foo foo = bar; , although the copies are actually made by direct-initialization, like m_a(a) above):

 

C:\Temp>type defaultcapturekittybyvalue.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 0;

    int y = 0;

 

    cout << "Input: ";

    cin >> x >> y; // EVIL!

 

    v.erase(remove_if(v.begin(), v.end(), [=](int n) { return x < n && n < y; }), v.end());

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 defaultcapturekittybyvalue.cpp > NUL && defaultcapturekittybyvalue

Input: 4 7

0 1 2 3 4 7 8 9

 

When the compiler sees x and y mentioned within the lambda, it captures them from main() by value.

 

 

What about (a), modifying the captured copies?  By default, a lambda's function call operator is const, but you can make it non-const by saying mutable:

 

C:\Temp>type capturekittybymutablevalue.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 1;

    int y = 1;

 

    for_each(v.begin(), v.end(), [=](int& r) mutable {

        const int old = r;

 

        r *= x * y;

 

        x = y;

        y = old;

    });

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

 

    cout << x << ", " << y << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 capturekittybymutablevalue.cpp > NUL && capturekittybymutablevalue

0 0 0 6 24 60 120 210 336 504

1, 1

 

This multiplies each element of v by the previous two elements.  (I had to think really hard to come up with an example that couldn't be expressed with partial_sum(), which could multiply by all previous elements, or adjacent_difference(), which could multiply by the immediately previous element.)  Note that (d) updates to the captured copies are not reflected in the local variables (again, ordinary value semantics).

 

 

What if you want to deal with (b), (c), and (d): avoid copies, observe updates to the local variables from within the lambda, and modify the local variables from within the lambda?  In that case, you want to capture by reference.  The syntax for doing this is the lambda-introducer [&x, &y] (you should think of this as X& x, Y& y ; that is, "reference" and not "address of"):

 

C:\Temp>type capturekittybyreference.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 1;

    int y = 1;

 

    for_each(v.begin(), v.end(), [&x, &y](int& r) {

        const int old = r;

 

        r *= x * y;

 

        x = y;

        y = old;

    });

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

 

    cout << x << ", " << y << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 capturekittybyreference.cpp > NUL && capturekittybyreference

0 0 0 6 24 60 120 210 336 504

8, 9

 

Note the differences from capturekittybymutablevalue.cpp: (1) the lambda-introducer [&x, &y] , (2) the absence of mutable , and (3) the local variables x and y having values of 8 and 9 at the end, reflecting their modification within the lambda.

 

 

This is being mentally translated into:

 

C:\Temp>type capturekittybyreference98.cpp

#include <algorithm>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

#pragma warning(push)

#pragma warning(disable: 4512) // assignment operator could not be generated

 

class LambdaFunctor {

public:

    LambdaFunctor(int& a, int& b) : m_a(a), m_b(b) { }

 

    void operator()(int& r) const {

        const int old = r;

 

        r *= m_a * m_b;

 

        m_a = m_b;

        m_b = old;

    }

 

private:

    int& m_a;

    int& m_b;

};

 

#pragma warning(pop)

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int x = 1;

    int y = 1;

 

    for_each(v.begin(), v.end(), LambdaFunctor(x, y));

 

    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));

    cout << endl;

 

    cout << x << ", " << y << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 capturekittybyreference98.cpp > NUL && capturekittybyreference98

0 0 0 6 24 60 120 210 336 504

8, 9

 

(When you use lambdas, the compiler automatically disables warning C4512 for lambda definitions.)

 

 

When you capture local variables by reference, the function object stores reference data members to those local variables.  This avoids copying, allows the function object to observe updates to those local variables, and allows the function object to modify those local variables through its references.  (Note that the function call operator is const, because we didn't say mutable, but that just prevents us from modifying the function object's data members.  The data members here are references and can't be modified anyways, but they can be used to modify what they refer to.  The constness of the function call operator is shallow, as always.)

 

Of course, if a lambda function object outlives local variables that it has captured by reference, you get crashtrocity.

 

 

Again, you can use default captures; the lambda-introducer [&] says "capture everything by reference".

 

What if you want to mix value captures and reference captures?  You could say [a, b, c, &d, e, &f, g] .  But you can also specify a capture-default and then override it for specific local variables.  Here's an example, modified from capturekittybymutablevalue.cpp:

 

C:\Temp>type overridekitty.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    int sum = 0;

    int product = 1;

 

    int x = 1;

    int y = 1;

 

    for_each(v.begin(), v.end(), [=, &sum, &product](int& r) mutable {

        sum += r;

 

        if (r != 0) {

            product *= r;

        }

 

        const int old = r;

 

        r *= x * y;

 

        x = y;

        y = old;

    });

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

 

    cout << "sum: " << sum << ", product: " << product << endl;

    cout << "x: " << x << ", y: " << y << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 overridekitty.cpp && overridekitty

overridekitty.cpp

0 0 0 6 24 60 120 210 336 504

sum: 45, product: 362880

x: 1, y: 1

 

Here, we want to capture x and y by value (because we want to modify them within the lambda, but not outside), while we want to capture sum and product by reference (because we do want to modify them outside).  The opposite lambda-introducer [&, x, y] would produce exactly the same result (capture everything by reference, except x and y by value).

 

 

Now, what if you want to do this?

 

C:\Temp>type memberkitty.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

class Kitty {

public:

    explicit Kitty(int toys) : m_toys(toys) { }

 

    void meow(const vector<int>& v) const {

        for_each(v.begin(), v.end(), [m_toys](int n) {

            cout << "If you gave me " << n << " toys, I would have " << n + m_toys << " toys total." << endl;

        });

    }

 

private:

    int m_toys;

};

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 3; ++i) {

        v.push_back(i);

    }

 

    Kitty k(5);

    k.meow(v);

}

 

C:\Temp>cl /EHsc /nologo /W4 memberkitty.cpp

memberkitty.cpp

memberkitty.cpp(12) : error C3480: 'Kitty::m_toys': a lambda capture variable must be from an enclosing function scope

 

 

The lambda expression syntax allows you to capture local variables, but data members aren't local variables.  By special dispensation, you can also capture this:

 

C:\Temp>type workingmemberkitty.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

class Kitty {

public:

    explicit Kitty(int toys) : m_toys(toys) { }

 

    void meow(const vector<int>& v) const {

        for_each(v.begin(), v.end(), [this](int n) {

            cout << "If you gave me " << n << " toys, I would have " << n + m_toys << " toys total." << endl;

        });

    }

 

private:

    int m_toys;

};

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 3; ++i) {

        v.push_back(i);

    }

 

    Kitty k(5);

    k.meow(v);

}

 

C:\Temp>cl /EHsc /nologo /W4 workingmemberkitty.cpp > NUL && workingmemberkitty

If you gave me 0 toys, I would have 5 toys total.

If you gave me 1 toys, I would have 6 toys total.

If you gave me 2 toys, I would have 7 toys total.

 

When you've captured this, you can say m_toys, which implicitly means this->m_toys as usual.  You could explicitly say this->m_toys too.  (Within a lambda expression, you can say this only if you've captured it; you can never get the this pointer for the lambda object itself.)

 

 

You can also capture this implicitly:

 

C:\Temp>type implicitmemberkitty.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

class Kitty {

public:

    explicit Kitty(int toys) : m_toys(toys) { }

 

    void meow(const vector<int>& v) const {

        for_each(v.begin(), v.end(), [=](int n) {

            cout << "If you gave me " << n << " toys, I would have " << n + m_toys << " toys total." << endl;

        });

    }

 

private:

    int m_toys;

};

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 3; ++i) {

        v.push_back(i);

    }

 

    Kitty k(5);

    k.meow(v);

}

 

C:\Temp>cl /EHsc /nologo /W4 implicitmemberkitty.cpp > NUL && implicitmemberkitty

If you gave me 0 toys, I would have 5 toys total.

If you gave me 1 toys, I would have 6 toys total.

If you gave me 2 toys, I would have 7 toys total.

 

You can also say [&] , but it doesn't affect how this is captured (always by value).  You can't say [&this] .

 

 

If you want a nullary lambda (taking no arguments), you can elide the lambda-parameter-declaration entirely:

 

C:\Temp>type nullarykitty.cpp

#include <algorithm>

#include <iostream>

#include <iterator>

#include <ostream>

#include <vector>

using namespace std;

 

int main() {

    vector<int> v;

 

    int i = 0;

 

    generate_n(back_inserter(v), 10, [&] { return i++; });

 

    for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });

    cout << endl;

 

    cout << "i: " << i << endl;

}

 

C:\Temp>cl /EHsc /nologo /W4 nullarykitty.cpp > NUL && nullarykitty

0 1 2 3 4 5 6 7 8 9

i: 10

 

This is two characters shorter than [&]() { return i++; } .  Whether eliding the lambda-parameter-declaration is good style is up to you to decide.

 

 

For laughs, this means that the following is valid C++0x:

 

C:\Temp>type nokitty.cpp

int main() {

    [](){}();

    []{}();

}

 

This constructs two do-nothing lambdas (one with a lambda-parameter-declaration, one without) and immediately calls them (that's the final set of empty parentheses).

 

 

Note that the optional lambda-parameter-declaration syntactically consists of:

 

( lambda-parameter-declaration-listopt ) mutableopt exception-specificationopt lambda-return-type-clauseopt

 

So, if you want to say mutable or -> ReturnType, you need empty parentheses between that and the lambda-introducer.

 

 

Finally, because lambdas produce ordinary function objects, you can store them in tr1::function:

 

C:\Temp>type tr1kitty.cpp

#include <algorithm>

#include <functional>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

using namespace std::tr1;

 

void meow(const vector<int>& v, const function<void (int)>& f) {

    for_each(v.begin(), v.end(), f);

    cout << endl;

}

 

int main() {

    vector<int> v;

 

    for (int i = 0; i < 10; ++i) {

        v.push_back(i);

    }

 

    meow(v, [](int n) { cout << n << " "; });

    meow(v, [](int n) { cout << n * n << " "; });

 

    function<void (int)> g = [](int n) { cout << n * n * n << " "; };

 

    meow(v, g);

}

 

C:\Temp>cl /EHsc /nologo /W4 tr1kitty.cpp > NUL && tr1kitty

0 1 2 3 4 5 6 7 8 9

0 1 4 9 16 25 36 49 64 81

0 1 8 27 64 125 216 343 512 729

 

 

auto

 

The auto keyword from C++98, where it did absolutely nothing, has been repurposed in C++0x for automatic type deduction.  When used in a declaration, it says "make the type of this thing the same as the type of whatever initializes it".  Behold:

 

C:\Temp>type autocat.cpp

#include <iostream>

#include <map>

#include <ostream>

#include <regex>

#include <string>

using namespace std;

using namespace std::tr1;

 

int main() {

    map<string, string> m;

 

    const regex r("(\\w+) (\\w+)");

 

    for (string s; getline(cin, s); ) {

        smatch results;

 

        if (regex_match(s, results, r)) {

            m[results[1]] = results[2];

        }

    }

 

    for (auto i = m.begin(); i != m.end(); ++i) {

        cout << i->second << " are " << i->first << endl;

    }

}

 

C:\Temp>cl /EHsc /nologo /W4 autocat.cpp > NUL && autocat

cute kittens

ugly puppies

evil goblins

^Z

kittens are cute

goblins are evil

puppies are ugly

 

map<string, string>::iterator, your decade-long reign of terror has come to an end!

 

(Note that m.begin() returns iterator, not const_iterator, because the map is not const.  C++0x cbegin() allows you to request a const_iterator from a non-const container.)

 

 

lambdas and auto

 

Earlier, I mentioned storing lambdas in tr1::functions.  But you shouldn't do that unless it's necessary, as tr1::function has some overhead.  If you want to reuse a lambda, or simply want to give it a name, you can use auto.  Here's an example, which also does something neat:

 

C:\Temp>type autolambdakitty.cpp

#include <algorithm>

#include <iostream>

#include <ostream>

#include <vector>

using namespace std;

 

template <typename T, typename Predicate> void keep_if(vector<T>& v, Predicate pred) {

    auto notpred = [&](const T& t) {

        return !pred(t);

    };

 

    v.erase(remove_if(v.begin(), v.end(), notpred), v.end());

}

 

template <typename Container> void print(const Container& c) {

    for_each(c.begin(), c.end(), [](const typename Container::value_type& e) { cout << e << " "; });

    cout << endl;

}

 

int main() {

    vector<int> a;

 

    for (int i = 0; i < 100; ++i) {

        a.push_back(i);

    }

 

    vector<int> b;

 

    for (int i = 100; i < 200; ++i) {

        b.push_back(i);

    }

 

    auto prime = [](const int n) -> bool {

        if (n < 2) {

            return false;

        }

 

        for (int i = 2; i <= n / i; ++i) {

            if (n % i == 0) {

                return false;

            }

        }

 

        return true;

    };

 

    keep_if(a, prime);

    keep_if(b, prime);

 

    print(a);

    print(b);

}

 

C:\Temp>cl /EHsc /nologo /W4 autolambdakitty.cpp

autolambdakitty.cpp

 

C:\Temp>autolambdakitty

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199

 

notpred is a negator lambda!  Note that we can't use C++98 <functional>'s not1(), as that requires your predicate to derive from unary_function, and lambdas don't.

 

 

static_assert

 

static_assert allows you to trigger compiler errors with custom error messages:

 

C:\Temp>type staticfluffykitten.cpp

template <int N> struct Kitten {

    static_assert(N < 2, "Kitten<N> requires N < 2.");

};

 

int main() {

    Kitten<1> peppermint;

 

    Kitten<3> jazz;

}

 

C:\Temp>cl /EHsc /nologo /W4 staticfluffykitten.cpp

staticfluffykitten.cpp

staticfluffykitten.cpp(2) : error C2338: Kitten<N> requires N < 2.

        staticfluffykitten.cpp(8) : see reference to class template instantiation 'Kitten<N>' being compiled

        with

        [

            N=3

        ]

 

 

If you have any questions, I'll be happy to answer them in the Comments.

 

Stephan T. Lavavej

Visual C++ Libraries Developer

  • A kind of a "personal curiosity" question here: are the newly supported C++0x features used internally in the upcoming Microsoft products? I understand that it's probably not the right place to ask such a general question - and you might not know whether, e.g. the Windows and Office teams (which, I would imagine, are two biggest C++ users in Microsoft) use them. But do you use them in the C++ compiler itself, for example, and/or in the libraries?

  • Stephan, thanks for the heads-up.  Looking forward to all the tasty deliciousness of VC10!

    I want it now!  I want it now!  Alas, probably won't be able to adopt C++0x-isms for my project for two more cycles (4 years).  Engineering bureaucracy.  :-(

    Kudos to the VC10 team (and expecting even more C++0x implementation goodness for VC11, of course), and to the JTC1/SC22/WG21 folks.

  • @int19h: I don't think any of the C++0x features will be used in the coming product wave. Those features are not yet stable for production use and developers also first needs to get used to it.

    Or how Steven Sinofsky said it:

    "We’re building a class library, not a compiler test suite, so there is no need to try to use all the features of C++."

    http://blogs.msdn.com/techtalk/archive/2005/08/18/453492.aspx

  • > C++0x Working Draft:

    > http://open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2798.pdf

    It looks like C++ 2003. I have't found any 0x improvements there.

  • > It looks like C++ 2003. I have't found any 0x improvements there.

    You have to look more attentively :) New additions are marked with cyan, removals are in striked-out red. For a few new things to look at - 5.1.1 is about lambdas; 6.5.4, range-based for-statement; 7.6, attributes; 14.9-10, concepts.

  • > int19h: I don't think any of the C++0x features will be used in the coming product wave. Those features are not yet stable for production use and developers also first needs to get used to it.

    Well, you can't avoid lambdas when using PPL - or are you saying that you guys aren't using PPL internally, either? What about "eating our own dog food"?

    Or are you saying that it's not yet stable at this specific moment, but will be sufficiently stable for internal use some time before VC10 is released?

    Personally, I would feel more comfortable using the release version of VC10 and PPL knowing that it was used by someone else to build successful production apps before me ;)

  • @int16h: I'm not affiliated with Microsoft, all MS employees have a [MSFT] behind their name.

    I also haven't looked at the Parallel Pattern Library yet.

    But Windows 7 will definitely not be build with the VC10 compiler, so no C++0x features. I'm not sure if they use the VC8 or VC9 compiler, I guess they've switched to the VC9 one. Maybe someone from the VC team can shed some light on it. I haven't seen the Win7 SDK yet.

  • [int19h]

    > 1) Are you going to implement the "range-based for-loop"

    The range-based for statement is powered by concepts (see N2798 6.5.4 [stmt.ranged]), so it won't be implemented in VC10.

    > given that you pretty much have it implemented already as "for each", it would be a fairly simple change to the syntax, no?

    The "for each" extension is highly limited; it calls .begin() and .end() (so it'll work with tr1::unordered_foo), but it's not customizable like the range-based for statement.

    > 2) Will nullptr take on its C++0x semantics, complete with nullptr_t?

    nullptr won't be implemented in VC10.

    > 3) As I understand, you'll be moving all the stuff from TR1 that makes reappearance in C++0x into namespace std.

    We're dragging TR1 components into namespace std with using-declarations.

    > What about the new stuff, such as std::unique_ptr?

    We're implementing some C++0x library features in VC10. I can confirm that unique_ptr and forward_list are being implemented. (Note that they've been checked into my branch, but they're not in the CTP. The CTP doesn't contain any C++ Standard Library changes.)

    [David]

    > VC has become so much better at implementing stadards - and doing it fast - that I am still happy.

    Glad to hear it!

    [Nikola Smiljanic]

    > Will VC10 support extern templates?

    No.

    [Jalf]

    > Shame other features like concepts didn't make it, but I can see why.

    Concepts were simply never on the table.

    > 1: These are the 0x core language features that'll make it in. How about library changes?

    This is my area. We're currently working on rewriting our Standard Library implementation for improved performance and C++0x conformance. For example, support for rvalue references is being added to our Standard Library implementation, which will trigger dramatic performance improvements in certain scenarios (and which will supersede VC8-9's limited and fragile Swaptimization). We're also picking up some new C++0x libraries, such as the aforementioned unique_ptr and forward_list (and my personal favorite, make_shared<T>()).

    > (And how about the new unicode char types?)

    That's a language feature, which won't be implemented in VC10.

    > 2: Can we expect to see more 0x support added over VC10's lifetime, through servicepacks and such?

    Magic 8 Ball says: Cannot predict now.

    [int19h]

    > are the newly supported C++0x features used internally in the upcoming Microsoft products?

    The limiting factor is how fast other teams can pick up the new compiler. VS itself is the first user, as the new compiler makes its way into all of our development branches. It takes longer for teams outside DevDiv, such as Windows and Office, to pick up the new compiler - also, they ship at different times, and are understandably loathe to pick up new toolchains late in their development cycles.

    > But do you use them in the C++ compiler itself

    JonCaves would know for sure, but this is my understanding: currently we don't, because of how our build system works. Our compiler builds itself, of course, but we start with a checked-in "Last Known Good" (or LKG) compiler. As the name implies, this a build which is known to behave reasonably. The LKG compiler is used to build the new "phase 0" compiler, and then the phase 0 compiler is used to build itself, producing the phase 1 compiler. If that happens without anything being mangled horribly, the compiler can compile itself and we're looking good. Periodically, the LKG build is updated, which is when new compiler features become available. (Due to how our build system works, the LKG compiler, and not the phase 1 compiler, is used to build the rest of VS.) After the next LKG update, the compiler itself (and anything else within VS, etc.) will be able to use rvalue references and other features unconditionally.

    > and/or in the libraries?

    Yes, although we currently guard uses of rvalue references with preprocessor machinery that detects whether we're being compiled with the LKG compiler or the rvalue reference-aware compiler. These guards will be removed after the next LKG update, which is when we can really start having some fun.

    [Andre]

    > Those features are not yet stable for production use

    Actually, they are quite stable. (There was an epic bug in template argument deduction that broke perfect forwarding with rvalue references, but it was found and fixed in time for the CTP.) If you find otherwise, prove me wrong by filing Microsoft Connect bugs! :-)

  • Thank you very much for detailed replies, Stephan!

    > VS itself is the first user, as the new compiler makes its way into all of our development branches.

    if VS uses the new features (now or soon), that's certainly good enough for me to sleep well ;)

    > We're dragging TR1 components into namespace std with using-declarations.

    Erm... I might be wrong, but isn't it going to Break Things (for template specialization purposes etc), unless you have "strong using" implemented, and are using it for that purpose?

    > If you find otherwise, prove me wrong by filing Microsoft Connect bugs! :-)

    Personally, I'm probably going to wait until there is a non-VPC CTP release before seriously playing with the new stuff. VPC is just too slow and inconvenient for me to deal with - it's good enough to quickly look at the new features, but experimenting is a pain.

    Then again, mmm, lambdas... I might just have to endure :)

  • @Stephan: I wasn't saying that the current CTP is buggy, but I doubt that Win7 will be build with a compiler from the development branch ;)

    Does the Windows devision use publicly released versions? How do they decide up to which label (LKG ?) they branch? Do they integrate only selected changelists thereafter? How is the workflow if someone from the Windows team finds a  bug in the compiler? I doubt they make fixes on their own in their branch but have to report them to get them fixed?

  • What do lambda expressions add to C++ besides making advanced use of the language even more difficult for humans to parse?  If I were in charge of a progamming group, I'd probably force people to use named functions to preserve readability.

  • @Sniffy,

    for small functors lambdas *are* more readable. Compare the "cout << n" sample where a lambda expression is used to the explicitly given struct LambdaFunctor with operator() overloaded. While reading the for_each line you have to lookup LamdaFunctor to see what kind of functor is called and what it does. The "inline" lambda lets you read everything in one place.

    --

    SvenC

  • @SvenC: I see the same problem with lambdas as with delegates in C#, code clustered through the whole app at places where it doesn't belong. But unlike delegates I don't think lambdas promote "unstructured" programming.

    But I think it will take a while before lambdas are well used. If I compare code written in the 90s with todays code there is a huge different, and so it will be with lambdas in the next years.

    The auto keyword is very useful for iterators, but I have the fear that novice users will use it the VB way :-<

  • [int19h]

    > Erm... I might be wrong, but isn't it going to Break Things (for template specialization purposes etc)

    That's a good question. I'll ask.

    [Andre]

    > Does the Windows devision use publicly released versions?

    I don't know very much about this (from where I'm sitting, Windows is in a galaxy far, far away, on the other side of the street), but they've definitely used hotfixed versions in the past.

    > How is the workflow if someone from the Windows team finds a  bug in the compiler?

    > I doubt they make fixes on their own in their branch but have to report them to get them fixed?

    I believe that they request hotfixes from us.

    [SvenC]

    > for small functors lambdas *are* more readable.

    Correct.

    Note that all of my lambdas aren't equally realistic; meow.cpp's [](int n) { cout << n << " "; } , cubicmeow.cpp's [](int n) { return n * n * n; } , and capturekittybyvalue.cpp's [x, y](int n) { return x < n && n < y; } are very realistic (the last one is a very compelling use for captures, I was rather pleased with it), but overridekitty.cpp's [=, &sum, &product](int& r) mutable { ... } is obviously a desperate attempt to demonstrate mixed value and reference captures (there *are* uses for this in real programs; distilling it down to a small example is difficult).

  • If you inspect other languages with closures (Standard ML, Haskell, Common Lisp, Ruby, Smalltalk ... whatever), you'll see that closures are a very integral and most of all SIMPLE thing to use. The feature you presented above is more or less the opposite of that.

    This new C++ standard has about as much reality as the new Fortran 2003 standard. C++ outlived its usefulness for application development.

Page 2 of 7 (91 items) 12345»