在本地定义的结构中模拟非本地(或自由)变量

时间:2013-11-08 21:46:34

标签: c++ c++11 lambda closures c++14

对于具有支持闭包的编程语言知识的人来说,这个问题可能才有意义。如果你不这样做,请不要评论“你为什么要这样做?”:有很多合理的理由这样做。

在函数式语言中常见的是定义捕获已定义局部变量的局部函数。在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?)

3 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分: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';
}

我认为未命名的struct的创建可能会由宏自动生成。

答案 2 :(得分: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,这种方法就有意义了。对于一次性的特殊用途,无论是否为本地,都无法避免编写专业类型。