【问题标题】:Is it possible to create function-local closures pre-C++11?是否可以在 C++11 之前创建函数局部闭包?
【发布时间】:2011-04-19 22:45:15
【问题描述】:

使用 C++11,我们可以获得 lambda,并且可以在我们真正需要它们的地方而不是在它们不真正属于的地方即时创建函数/函子/闭包。
在 C++98/03 中,制作函数局部函子/闭包的好方法如下:

struct{
  void operator()(int& item){ ++item; }
}foo_functor;
some_templated_func(some_args, foo_functor);

遗憾的是,您不能对模板使用本地类型(Visual Studio 允许在启用语言扩展的情况下这样做)。然后我的火车走了以下路:

struct X{
  static void functor(int& item){ ++item; }
};
some_templated_func(some_args, &X::functor);

明显的问题是,您不能保存任何状态,因为本地结构/类不能有静态成员。
我下一个解决这个问题的想法是使用std::bind1ststd::mem_fun 以及非静态方法和变量的组合,但不幸的是std::mem_fun 不知何故被std::mem_fn(&X::functor) 窒息,这又可能是因为本地结构/类可以'不能在模板中使用:

// wanted, not working solution
struct X{
  int n_;
  X(int n) : n_(n) {}
  void functor(int& item) const { item += n_; }
};
X x(5);
some_templated_func(some_args,std::bind1st(std::mem_fun(&X::functor),&x));

在 VC9 和 VC10(使用 /Za,禁用语言扩展)下失败并出现以下错误

error C2893: Failed to specialize function template 'std::const_mem_fun1_t<_Result,_Ty,_Arg> std::mem_fun(_Result (_Ty::* )(_Arg) const)'
With the following template arguments:
'void'
'main::X'
'int &'

或在 gcc 4.3.4 下出现此错误

error: no matching function for call to ‘mem_fun(void (main()::X::*)(int&))’

有趣的是,即使启用了语言扩展,VC9 / VC10 仍然对上述示例感到窒息:

error C2535: 'void std::binder1st<_Fn2>::operator ()(int &) const' : member function already defined or declared

那么,标题中所述的功能是否可以以某种方式实现?还是我在最后一个示例中我如何使用std::bind1ststd::mem_fun 时犯了错误?

【问题讨论】:

  • std::mem_fn(&amp;X::functor) 阻塞,因为functor 不是成员函数,它是静态的。
  • @GMan:不,不是在最后一个例子中。
  • @Xeo:对不起,我的意思是回答“但不幸的是 std::mem_fun 不知何故窒息......”,在第二个。
  • @Xeo:它必须是静态的,否则你最终会得到一个指向本地成员类型的指针,这不是一个有效的模板参数。请改用std::ptr_fun,并使用bind_1st 传递实际捕获的值,而不是本地类型的实例。
  • @GMan:啊,是的,应该有一个新段落和一个双冒号,对不起。

标签: c++ function closures local functor


【解决方案1】:

bind1st 仅适用于二进制函数,并且通常受到很大限制。 mem_fn 仅适用于非静态成员函数;对于您的应用程序,您需要ptr_fun

真正在 C++03 中完成这项工作的最佳工具是 Boost Bind,或者我将在此处使用 tr1::bind 进行演示,这(在我看来)更便携。

#include <tr1/functional>
#include <iostream>
#include <algorithm>

using namespace std::tr1::placeholders;

int nums[] = { 1, 2, 4, 5, 6, 8 };

int main() {
    struct is_multiple {
        static bool fn( int mod, int num ) { return num % mod == 0; }
    };

    int *n = std::find_if( nums, nums + sizeof nums/sizeof*nums,
                        std::tr1::bind( is_multiple::fn, 3, _1 ) );

    std::cout << n - nums << '\n';
}

【讨论】:

  • 如果是本地类,你会怎么用?您不能将其传递给任何其他函数(void* 除外?)。
  • 这是我在问题中提出的第一个示例,如果它是本地类/结构,则不能将该仿函数传递给在仿函数上模板化的函数。 :|
  • @Ben:啊,我忘了!实际上我已经在非模板上下文中使用了这个成语,但这不适用于 OP 的问题。
  • @Xeo:啊,现在我更好地理解了你的目标,以及你为什么要使用bind1st。是的,bind1st 应该可以解决你的问题,如果你能让它正常工作的话。问题是它是如此痛苦。我会稍微修改一下答案。
  • 编辑后的版本很像我想要的。太糟糕了,您需要整个绑定的东西,但至少它可以让您以某种合理的方式模拟函数局部闭包。感谢那! :)
【解决方案2】:

是的,你可以,但你必须实现一个或多个在接口中声明的虚拟方法。

template<typename Arg, typename Result>
struct my_unary_function
{
    virtual Result operator()(Arg) = 0;
};

template<typename Arg, typename Result>
struct my_unary_functor
{
    my_unary_function<Arg, Result> m_closure;
    my_unary_functor(my_unary_function<Arg, Result> closure) : m_closure(closure) {}
    Result operator()(Arg a) { return m_closure(a); }
};

template<typename T, TFunctor>
void some_templated_function( std::vector<T> items, TFunctor actor );

然后你可以定义和使用局部闭包:

void f()
{
    std::vector<int> collection;

    struct closure : my_unary_function<int, int>
    {
        int m_x, m_y;
        closure(int x, int y) : m_x(x), m_y(y) {}
        virtual int operator()(int i) const { cout << m_x + m_y*i << "\t"; return (m_x - m_y) * i; }
    };

    some_templated_function( collection, my_unary_functor<int,int>(closure(1, 5)) );
}

感谢@Omnifarious 的改进(不需要my_unary_functor):

void f()
{
    std::vector<int> collection;

    struct closure : my_unary_function<int, int>
    {
        int m_x, m_y;
        closure(int x, int y) : m_x(x), m_y(y) {}
        virtual int operator()(int i) const { cout << m_x + m_y*i << "\t"; return (m_x - m_y) * i; }
    };

    // need a const reference here, to bind to a temporary
    const my_unary_functor<int,int>& closure_1_5 = my_unary_functor<int,int>(closure(1, 5))
    some_templated_function( collection, closure_1_5 );
}

【讨论】:

  • 我想我知道你在说什么,这会奏效,但它有点丑陋和慢。是的,这几乎就是我的想法。
  • 啊,我现在明白了。但这需要我将我想使用的每个模板化函数包装在一个新函数中,该函数采用my_unary_function&lt;Arg,Ret&gt;,对吗?
  • @Xeo:假设您调用的通用算法已经设计为使用接口。如果它是为仿函数设计的,那就不太好,但我可以构建一个非本地仿函数/接口适配器类,然后不需要单独包装每个函数。
  • @Xeo:不一定。您可以像这样创建一个临时文件:my_unary_function&lt;int, int&gt; &amp;expr = closure(1, 5);,然后传递该临时文件。它的类型将是正确的类型,您不必调用模板函数的特殊版本。
  • @Omni:啊,没错。是的,这当然解决了编写包装函数的问题。谢谢。
【解决方案3】:

如果这在 C++03 中可行,为什么 C++0x 会引入 lambda? lambda 存在是有原因的,因为绑定和所有其他 C++03 解决方案都非常糟糕。

【讨论】:

  • 嗯,可能是 C++0x 刚刚引入了一种更简单的解决方案。 :)
  • 例如,C++0x 中的 ranged-for 结构在 C++03 中是完全可行的,但是 ranged-for 就更好了。
  • 这里的想法是对的,但推理是错误的。如果它是可行的,我们现在可能会如何在 C++03 中伪造它,但我们没有。 (即使我们可以,C++0x 仍然会添加更好的语法。)
  • @Omnifarious:ranged-for 循环是一种可怕的语言结构,原因有很多。 @GMan:标准委员会不会浪费时间介绍非问题的解决方案。好吧,排除了 ranged-for 循环。
  • @DeadMG:为什么远程攻击这么可怕?
猜你喜欢
  • 1970-01-01
  • 2016-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-25
  • 1970-01-01
相关资源
最近更新 更多