译文

VC10中的C++0x特性 Part 1 (2) : Lambdas,auto,以及 static_assert

翻译:飘飘白云 | 2009-05-29 10:10:52 | 阅读542 | 来源

VC10中的C++0x特性 Part 1 (2) : Lambdas,auto,以及 static_assert

 

来源:vcblog 翻译:飘飘白云 kesalin@gmail.com

 

共三页: 第一页 本页 第三页

 

情形(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

 

在这里,我们想通过按值传递 xy (因为我们只想在 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 参数声明,一个没有)并立即调用它们(通过最后一个空括号组合)。

 

< 前一页   本页   后一页

分享:

标签:C++0x,

添加评论