0

这个问题可能只对了解支持闭包的编程语言的人有意义。如果您不这样做,请不要评论“您为什么要这样做?”:这样做有很多正当理由。

在函数式语言中,定义捕获已经定义的局部变量的局部函数是很常见的。在 C++ 中,这看起来像(但当然是非法的):

#include <iostream>
using namespace std;

int main()
{
    int x = 0;
    int f() { return x + 1; }

    cout << f() << endl; // would print 1

    x = 2;
    cout << f() << endl; // would print 3
}

为了实现这一点,C++11 引入了lambda 函数,因此实际上可以以一种相当不错的方式来实现它(虽然,不像函数式语言中那样好 ;-)):

#include <iostream>
using namespace std;

int main()
{
    int x = 0;
    auto f = [&] () { return x + 1; };

    cout << f() << endl; // actually compiles and prints 1

    x = 2;
    cout << f() << endl; // actually compiles and prints 3
}

我的问题是:现在可以通过引用来自动捕获函数的自由变量,对于本地定义的结构来说这不是很好吗?理想情况下,我希望能够写:

int main()
{
    int x = 0;

    struct A 
    {
        int y;
        A(int y) : y(y) {}

        int f() { return x + y; };
    };

    A a1(1);
    A a2(2);

    cout << a1.f() << endl; // would print 1
    cout << a2.f() << endl; // would print 2

    x = 2;
    cout << a1.f() << endl; // would print 3
    cout << a2.f() << endl; // would print 4
}

我发现的唯一解决方法是将所有非本地(自由)变量作为参数手动传递给构造函数,当它们很多时,这有点痛苦:

#include <iostream>
using namespace std;

int main()
{
    int x = 0;

    struct A 
    {
        // meaningful members
        int y;
        int f() { return x + y; };

        // free variables
        int & x;

        // Constructor
        A(
            // meaningful arguments
            int y,

            // capturing free variables
            int & x

        ) : y(y), x(x) {}
    };

    A a1(1, x);
    A a2(2, x);

    cout << a1.f() << endl; // prints 1
    cout << a2.f() << endl; // prints 2

    x = 2;
    cout << a1.f() << endl; // prints 3
    cout << a2.f() << endl; // prints 4
}

您是否知道任何其他解决方法可以避免手动将所有自由变量作为参数传递,或者您是否知道这些“环境感知”本地定义的结构是否被考虑用于 C++ 的未来扩展?(即,C++1y?)

4

3 回答 3

1

我不觉得这特别漂亮,我也不完全确定它是否合规,但 g++ 和 clang++ 都没有抱怨这一点:

#include <iostream>

int main()
{
    int x = 1;

    auto l = [&](int p){
        auto ll0 = [&, p]{ return p + x + 5; };
        auto ll1 = [&, p]{ return p + x * 2; };

        struct
        {
            decltype(ll0) l0;
            decltype(ll1) l1;
        } ret{ll0, ll1};

        return ret;
    };

    std::cout << l(42).l0() << '\n';

    auto lo = l(21);
    std::cout << lo.l1() << '\n';
}

我认为未命名结构的创建可能由宏自动完成。

于 2013-11-08T22:44:46.133 回答
1

C++ 的 lambda 表达式是一种捕获机制和内联对象字面量。根据您的确切目的,它们可能比本地结构定义更方便。

作为一个激励性的例子,请考虑以下内容:

// environment
int offset = 42;

struct local_type {
    // capture environment 'by-hand'
    int offset;

    // purpose of the local type is to expose two overloads
    int operator()(int x) const
    { return x + offset; }

    double operator()(double x) const
    { return x + offset; }
} f { offset };

您可以通过以下方式扭转局面:

int offset = 42;
auto f = make_overload([=](int x) { return offset + x; },
                       [=](double x) { return offset + x; });

lambda 表达式负责捕获,make_overload组合器负责构建所需的对象——这里是一个重载的operator(). (最好通过使用继承来实现。)

make_overload如果您知道您将在不同的地方(重新)使用这种方法是有意义的。对于一次性的特殊用途,无论是否在本地,都无法避免编写专业类型。

于 2013-11-10T05:21:40.620 回答
1

您要求的内容不可用,但您可以通过将函数与 lambdas 和 binders 的组合组合来获得类似的结果:

auto lambda = [](int i) { return x+i; };
auto a1 = std::bind(lambda,1);
auto a2 = std::bind(lambda,2);

根据更改的数量和形状,您可以反转解决方案并拥有一个结构,该结构将 lambda 与捕获一起使用,然后添加它自己的逻辑。

于 2013-11-08T21:52:20.463 回答