【问题标题】:std::function target method in Visual Studio 2012Visual Studio 2012 中的 std::function 目标方法
【发布时间】:2014-05-02 18:28:43
【问题描述】:

我有以下代码:

#include <functional>

//...

typedef int (*Myfun)(int);
std::function<int (int)> fn0([](int a)->int {
    return -a;
});
std::cout << "val == " << fn0(3) << std::endl; //"val == -3"
Myfun *fptr = fn0.target<Myfun>(); //fptr is NULL!!
std::cout << "val == " << (*fptr)(3) << std::endl; //Access violation error

实际上,这段代码是来自MSDN 的代码,略有改动:使用 lambda 代替普通函数。

为什么调用fn0.target&lt;Myfun&gt;()返回NULL?

当我声明一个常规函数时

int neg(int val) { 
    return (-val); 
}

然后写std::function&lt;int (int)&gt; fn0(neg);,似乎一切正常,但是 lambda 没有被正确处理。

【问题讨论】:

    标签: c++ visual-studio-2012 c++11 lambda


    【解决方案1】:

    typedef int (*Myfun)(int); 中的Myfun 类型与函数目标的类型无关,它是执行表达式[](int a)-&gt;int { return -a; } 产生的闭包对象的唯一、未命名类型

    尝试执行std::cout &lt;&lt; fn0.target_type().name() &lt;&lt; '\n'; 自己看看。

    当你用int neg(int val) { return (-val); } 声明一个函数时,neg 的类型正好是Myfun(在函数到指针的转换之后,发生在std::function&lt;int(int)&gt; fn0(neg)),这就是为什么std::function::target() 能够返回一个指向它的指针。

    【讨论】:

    • 在我体验过函数语言之后,lambda 函数与常规函数几乎相同,签名相同,这对我来说似乎很自然。有什么方法可以执行我在这里尝试做的事情吗?我的最后一个任务是将 capturing lambda(使用 [this])转换为行函数指针。
    • @Cruel_Crow 不清楚您要做什么。你需要一个原始函数指针做什么?作为回调传递给 C API?
    • @Cruel_Crow 在 C 函数中没有存储来保存捕获的参数,您需要某种包装器。对此有很多问答,例如stackoverflow.com/questions/18169180/…
    • 其实,我找到了一个相对“不痛不痒”的解决方案:我在 lambda 之前声明了一个我想要传递的值的临时静态变量。当然有它的局限性,但它是一些东西。
    【解决方案2】:

    Cubbi 解释了为什么您的代码不起作用——lambda 不是函数指针。

    现在,普通的 lambda 可以转换为函数指针。那么您是否真的想强制进行这种转换?

    template<typename F>
    struct as_pointer_t {
      F f;
      template<typename R, typename... Args>
      operator type<R(*)(Args...)>() const { return {f}; }
      template<typename R, typename... Args>
      operator std::function<R(Args...)>() const { return (R(*)(Args...))f; }
    };
    template<typename F>
    as_pointer_t<F> as_pointer( F&& f ) { return {std::forward<F>(f)}; }
    

    现在我们可以这样做了:

    int main() {
      typedef int (*Myfun)(int);
      std::function<int (int)> fn0(as_pointer([](int a)->int {
        return -a;
      }));
      std::cout << "val == " << fn0(3) << std::endl; //"val == -3"
      Myfun *fptr = fn0.target<Myfun>(); //fptr is no longer NULL!!
      std::cout << "val == " << (*fptr)(3) << std::endl;
    }
    

    并且您的代码按预期工作。但是,只有当您的 lambda 没有捕获任何内容时,上述内容才会编译。

    如果您的目标是将捕获的 lambda 转换为函数指针,则不能。您可以将状态存储在全局变量中,并在非捕获 lambda 中使用它。您还可以将捕获的 lambda 转换为函数指针和 void* 对。

    我编写的代码采用编译时索引将void* 注入列表(以及使用可选类型而不是void*),并生成所述一对void* 和函数指针。一般情况很棘手——具体情况(比如第一个参数)要容易得多。

    template<typename T> using type=T;
    
    template<typename F, typename X=void*>
    struct callback_t {
      F f;
      operator X() { return X(&f); }
      template<typename R, typename...Args>
      operator type<R(*)(X, Args...)>() const {
        return []( X x, Args... args )->R {
          F* f = (F*)(x);
          return (*f)(std::forward<Args>(args)...);
        };
      }
    };
    template<typename X=void*, typename F>
    callback_t<F,X> make_callback( F f ) {
      return {std::forward<F>(f)};
    }
    

    使用:

    typedef void(*pfun)(void*, int);
    void call_pfun( pfun f, void* p) {
      for (int i = 0; i < 3; ++i)
        f( p, i );
    }
    int main()
    {
      int y = 7;
      auto callback = make_callback([y]( int x ) { std::cout << x+y << "\n"; });
      call_pfun( callback, callback );
    }
    

    live example.

    【讨论】:

    • 谢谢,这个例子太棒了!但是,我认为我不会在实际程序中使用此示例。相反,我更喜欢等到 C++14 ——也许在本地就有可能,谁知道呢。
    猜你喜欢
    • 2011-01-26
    • 1970-01-01
    • 1970-01-01
    • 2012-09-13
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    • 1970-01-01
    相关资源
    最近更新 更多