罗朝辉(飘飘白云)

关注嵌入式操作系统,移动平台,图形开发。-->加微博 ^_^

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  85 随笔 :: 0 文章 :: 169 评论 :: 0 Trackbacks

【译】VC10中的C++0x特性 Part 1:Lambdas,auto,以及 static_assert

来源:vcblog 作者:Stephan T. Lavavej 翻译:飘飘白云  

(转载时请注明作者和出处。未经许可,请勿用于商业用途)

简介

这一系列文章介绍Microsoft Visual Studio 2010 中支持的C++ 0x特性,目前有三部分。
Part 1 :介绍了Lambdas, 赋予新意义的auto,以及 static_assert;
Part 2( , , ):介绍了右值引用(Rvalue References);
Part 3:介绍了表达式类型(decltype)

VC10中的C++0x特性 Part 1,2,3 译文打包下载(doc 和 pdf 格式): 点此下载


本文为 Part 1。

Microsoft Visual Studio 2010 九月社区技术预览版 (CTP)所带的Visual C++编译器对四个C++0x语言特性提供了支持,也就是 lambdas,auto,static_assert,以及 rvalue references (右值引用,译注:后面不再对这个词进行翻译)。今天,我将详细介绍前三个特性。(很快我将贡献一整篇幅的文章来解释右值引用,仅仅是因为再在这里解释的话将会加大这篇已经很长的文章的篇幅)

 


首先,说明一些事情:


1,今天的这篇文章是由 Stephan T. Lavavej,Visual C++库的开发人员以及C, A, 与 T读者投书栏带给你们的。注意作为库的开发人员,我并没有实现这些特性。那是 Jonathan Caves,前端编译器开发者,选举标准委员会成员以及所有“忍者”(鲜为人知的高手)的成果。

2,我将 Visual C++ compiler in VS 2010 简称为 VC10 ( VS 2008 包含 VC9,VS 2005 包含 VC8,等等。 - 10 并不比 2010 简短)

3,C++0x 指的是即将到来的 C++ 标准,现在还在起草中。(C++标准委员会希望它可以在 2009 年完成,称作 C++ 09;玩笑话说如果它推迟到 2010 或者更晚的话,“x” 将是十六进制的了)。 C++ 98 和C++ 03 指的是当前的 C++ 标准。(在这里不回顾历史了, C++ 标准 2003 仅仅是最初的 C++ 1998 标准 的“补丁”版,对大部分人来说可以忽略两者间的区别。C++ 03 和 C++ 0x 模样虽然看起来差不多,但完全不同)

4,我要感谢标准委员会开发出这些奇妙而有用并富有艺术的特性。他们也在以下站点上提供了重要的文档:

C++0x 语言特性:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2705.html

C++0x 库特性:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2706.html

C++0x 进行中的草案:

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


5,总是会有bug的(虽然希望不会太多),这也就是发布 CTP 版本的主要目的(让用户测试发现 bug )。请通过 Microfsoft 把这些 bug 报告给我们。

现在,让我们来审视这些特性吧!

 lambdas

在 C++ 0x 中,“lambda 表达式”隐式定义并构建不具名函数对象,这些对象就像手写函数对象一样。下面是 lambda “Hello,World”入门级的示例:

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


[] 操作符是 lambda 导引符, 它告诉编译器一个 lambda 表达式开始了。 (int n) 是 lambda 参数声明,它告诉编译器不具名函数对象类的函数调用操作符带有哪些参数, { cout << n << " "; }  是复合声明,它是不具名函数对象类的函数调用操作符的函数体。不具名函数对象类的函数调用操作符默认返回 void。


这样,C++0x 在内部将它转换成如你在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


现在我将不再累述类似“不具名函数对象类的函数调用操作符默认返回 void”这样的话,开始换用“lambda 函数返回 void”的说法,但是记住 lambda 表达式做了些什么是很重要的,那就是:定义类并构建对象。


当然,lambda 的复合声明部分(函数体部分)可以包含多个声明语句,譬如:


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

 

lambda 函数也并不总是必须返回 void。如果 lambda 的复合声明语句像是这样的 { return expression; } ,那么 lambda 的返回类型就会自动地被推断成 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

 

在这里,  n * n * n 的类型是 int,所以 lambda 函数返回 int。

有着复杂复合声明语句的 lambda 函数不会自动推断返回类型,你必须显式指定返回类型。


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


 -> double 是可选的 lambda 返回类型从句。为什么它不放在左边(译注:返回类型一般在函数左边声明),就像程序员一直以来在C函数中做的那样?因为那样的话 lambda 导引符 [] 就不会第一个出现了,而正是它告诉编译器一个 lambda 函数开始了。(核心工作组最擅长解决这样的问题;尝试猜测C++ 中一个给定的概念是否是可被解析的会让我头疼。)


如果忘记了指定 lambda返回类型从句,编译器就会抱怨每一个返回语句:


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


到目前为止我所介绍的 lambda 都是无状态的:它们不包含数据成员。你也可以有有状态的 lambda,这是通过“传递”(原文用加引号的 capturing 这个词,在这里我翻译成传递似乎不太妥,故我都加括号引用原文,下同)局部变量来实现的。空的 lambda 导引符 [] 意味着“一个无状态的 lambda”,但在 lambda 导引符 [] 中你可以指定 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


如果你忘记了capture-list,编译器就会抱怨:


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


(我很快就会解释默认的传递(capture))


记着,lambda 表达式隐式地定义了一个不具名函数对象类。复合声明语句 { return x < n && n < y; } 在这个类中被当作函数调用操作符的函数体。虽然从词法结构上看复合声明语句是在 main() 块之内,但在概念上它是在 main() 块之外的,因此如果不传递(capture)到 lambda 中去,就不能在其中使用来自main() 中的局部变量。


上面的代码在内部被翻译成:

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


在这里你可以清楚地看到是“按值”传递(captures)的。函数对象存储了局部变量的拷贝。这就使得函数对象可以比通过传递(capture)来创建它们的局部变量有更长的生命期。但是,要注意:(a)在 lambda 中不能修改通过传递(capture)获得的拷贝,因为默认情况下函数调用操作符是 const 属性的,(b)一些对象的拷贝开销是昂贵的,(c)局部变量的更新不会反应到通过传递(capture)获得的拷贝(在语义上它们是原始值)。很快我就会解释如有需要应该如何来处理以上情况。


但是首先,你可以“按值传递(capture)任何东西”,而不用特别指明每一个你想要传递(capture)的局部变量。其语法是使用这种形式的 lambda 导引符 [=] (默认传递(capture-default) = 应该可以让你想起赋值或者拷贝初始化 Foo foo = bar; 虽然这里的拷贝实际上是直接初始化(通过初始化列表进行赋值),就像上面的 m_a(a))。


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


当编译器看到 lambda 中的 x 和 y, 就会从 main() 中按值传递(capture)。


情形(a)要修改通过传递(capture)获得拷贝该怎样呢?默认情况下,一个 lambda 函数调用操作符是 const 属性的,但是可以通过使用 mutable 把它变成 non-const。


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


这里是依次将 v 中前两个元素相乘。(我得承认真的很难想出一个不用 partial_sum() 或 adjacent_difference() 表示的例子,partial_sum() 是与前面所有的元素相乘,adjacent_difference()是与前一个元素相乘)。注意到情形(d),对通过传递获得的拷贝的更新操作并没有影响局部变量(再一次,原始值语义)。


如果你想处理情形(b),(c)和(d):避免拷贝,在 lambda 中观察局部变量的更新,以及在 lambda 中修改局部变量又该怎么做呢?在这种情况下,你会想通过引用传递(capture by reference)。其语法是这种形式的 lambda 导引符 [&x, &y] (你可以把它想象成  X& x, Y& y ; 那是“引用”而不是“取址”)。


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


注意与 capturekittybymutablevalue.cpp 的区别:(1),lambda 导引符是 [&x, &y] ,(2)没有 mutable,(3),局部变量 x 和 y 最后的值是 8 和 9,反应了在 lambda 中对他们的修改。


上面的代码在内部被翻译成:


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


当你通过引用传递(capture)局部变量时,函数对象存储局部变量的引用。这样避免了拷贝,能够让函数对象观测对局部变量的更新,以及能够让函数对象通过引用修改局部变量。(注意函数调用操作符是 const 属性的,因为我们没有把它声明成 mutable,但这只阻止我们修改函数对象的数据成员。在这里数据成员是引用,我们无法修改它,但是我们可以修改它所指向的东西。函数调用操作符的常量性是,且一直都是,表面的)

当然,如果函数对象比用于引用传递的局部变量的生命期更长的话,程序会毁灭性地崩溃。(译注:就是说引用所指向的东西已经销毁了,但你可能还在使用这个引用)


同样,你可以使用默认的按引用传递语法;这种形式 lambder 导引符 [&] 表明“按引用传递”。


如果你想混合使用按值传递和按引用传递该怎么做呢?你可以像这样声明 [a, b, c, &d, e, &f, g],但你也可以指定默认按值传递,再为特定的(需要按引用传递的)局部变量而重载它。下面是一个修改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


在这里,我们想通过按值传递 x 和 y (因为我们只想在 lambda 内部修改它们,而影响外部),同时我们想按传引用捕获 sum 和 product(因为我们确实想让对它们的修改在外部也生效)。像这样的 lambda 导入符组合 [&, x, y] 可以起到同样的效果(按应用传递所有参数,除了 x 和 y 是按值传递之外)


嗯,如果你想这么做又该如何?

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


lambda 表达式语法允许你传递局部变量,但是数据成员不是局部变量。通过特别的方法,你也可以传递数据成员:


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.


当你传递 this,你可以使用 m_toys,它隐含意味着 this->m_toys,就如平常一样(译注:作为类自身成员变量)。你也可以显式使用 this->m_toys。(在一个 lambda 表达式内,只有当你传递了 this,你才能使用它;你永不可能取得 lambda 对象它自身的 this 指针)。


你也可以隐式地传递 this :


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.

你可以使用 [&],但它对传递是怎样进行的(默认是按值)没有影响。你不能使用 [&this]。

如果你想使用一个空无的 lambda (不带参数),你可以省略整个 lambda参数声明:


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


这比 [&]() { return i++; } 少两个字符。省略 lambda 参数声明是否是良好的风格是由你来决定的。


仅博一笑,这意味着下面的代码在 C++0x 中是合法的。


C:\Temp>type nokitty.cpp

int main() {

    [](){}();

    []{}();

}

 

上面构建了两个什么也不做的 lambda (一个有 lambda 参数声明,一个没有)并立即调用它们(通过最后一个空括号组合)。


注意可选的 lambda 参数声明语法上包括:


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


因此,如果你想使用 mutable 或  -> ReturnType,你需要在 lambda 导引符和它们之间插入空括号。


最后,因为 lambda 产生普通的函数对象,你可以在 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


关键词 auto 是从 C++ 98 得来的,它在 C++ 98 中实际上并没起什么作用,但在 C++ 0x 中被重用于自动类型推导(automatic type deduction)。 在一个声明中使用 auto ,意味着“让它和初始化它的事物具有相同的类型”。你看:


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,你近10年的恐怖统治终于到头了。

(注意:m.begin()返回 iterator,而不是 cosnt_iterator,因为 map 不是 const 属性的。C++ 0x cbegin() 允许你从一个 non-cosnt 容器中获得一个 const_iterator。)


lambdas and auto


先前我提到过在 tr1::functions 中存储 lambda 函数。但是如非必要请别这么做,因为 tr1::function 会增加一些开销。如果你想重用 lambda 函数,或只想给它命名的话,你可以使用 auto。这儿有一个简洁的例子:


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 是一个否定 lambda 函数,注意我们不能使用 C++ 98 <functional>中的 not1() 函数,因为那要求你的谓词函数继承自 unary_function,而 lambda 函数并不满足这个条件。


static_assert


static_assert 让你能够在触发编译器错误时使用定制的错误信息:

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

        ]


如果你有任何疑问,我很乐意在评论中回答它们。


Stephan T. Lavavej

Visual C++ Libraries Developer

Published Tuesday, October 28, 2008 9:31 AM by vcblog


(转载时请注明作者和出处。未经许可,请勿用于商业用途) 

posted on 2009-05-28 11:27 罗朝辉 阅读(4696) 评论(14)  编辑 收藏 引用 所属分类: C/C++

评论

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-28 12:34 Chipset
这点微不足道的特性几乎可以忽略不说。  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-28 13:25 Etrnls
@Chipset
lambda是微不足道的?  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-28 14:37 尹东斐
恩,你全文翻译了,敬佩中,这三篇虽然看了,我觉得rvalue reference比较有意思。

当然楼上说这些特性微不足道,是因为他还没到能看出来这些特性强大之处的境界~~

不过不知道为啥vc10不支持conception,这个鄙人认为是最强大的特性之一了,有了这个,就可以和那种上百KB的error message说再见了。  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-28 14:39 飘飘白云
使用 lambda 的代码更加简洁,很少有人愿意单独去整个函数对象来使用 for_each  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-28 19:49 playcpp
喜欢C++编程的朋友 请加入QQ群 36071431 有很多人和你一起讨论解决问题(还有40个名额) 。  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-28 21:32 bneliao
写得很清晰,翻译很流畅,赞。

  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-29 02:02 Pear
微不足道?我的天!当我得知2010支持C++0x的特性时就立刻起了一身鸡皮疙瘩了哇  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-30 08:37 dopoco
C++想把事情变简单点儿,却把C++本身弄得越来越晦涩难懂,C++就是C++,干嘛非要和JAVA、C#靠齐  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-30 23:28 eXile
lambda是个令人欣喜的特性,至于右值引用,可以极大地提高标准库的效率,但是对一般的程序员没有什么太大影响,不好理解的话,就当它不存在好了。  回复  更多评论
  

# re: 【译】VC10中的C++0x特性:Lambdas,auto,以及 static_assert 2009-05-31 21:39 飘飘白云
总结下:
lambda 更好地支持函数式编程
rvalue 提高效率
static_assert 在编译期间即可 assert 而不再需要等到运行时
auto 可以让程序员偷点懒,还可以自动类型推导,并且可在编译期间确定类型
  回复  更多评论
  

# re: 【译】VC10中的C++0x特性 part 1:Lambdas,auto,以及 static_assert 2009-06-08 01:25 狼狗
@飘飘白云
为何lambda在value capture下可以修改静态外部变量?
为何对静态变量和非静态变量有不同处理策略?
例如:

#include <iostream>
#include 
<algorithm>
using namespace std;

int main()
{
    
static int v = 0;
    
char ch[2009];
    for_each(ch, ch 
+ sizeof(ch), [=](char c) {v++;});
    cout
<<v<<endl; // v=2009
}

.   回复  更多评论
  

# re: 【译】VC10中的C++0x特性 part 1:Lambdas,auto,以及 static_assert 2009-06-08 10:17 罗朝辉
@狼狗

这是因为是 static 的缘故,static 变量在内存中始终都只有一份,它在这里相当于全局的,和非静态局部变量有着本质不同。  回复  更多评论
  

# re: 【译】VC10中的C++0x特性 part 1:Lambdas,auto,以及 static_assert 2009-06-09 15:49 狼狗
感觉你没有回答我的问题啊:为何对静态变量和非静态变量有不同处理策略?
static变量就不可以在lambda内有一个拷贝吗?明显是可以的。
那么为何要采用这种使得capture规则不完美的策略呢?
  回复  更多评论
  

# re: 【译】VC10中的C++0x特性 part 1:Lambdas,auto,以及 static_assert 2009-06-09 17:06 罗朝辉
@狼狗

我猜是为了保持 static 语义的一致性吧,就如 val 一样:

void foo()
{
static int val = 0;
val++;

}  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理