【问题标题】:Transforming a two-variable std::function to a single-variable one将二变量 std::function 转换为单变量
【发布时间】:2015-06-18 02:50:14
【问题描述】:

我有一个函数,它获取两个值 x 和 y,并返回结果:

std::function< double( double, double ) > mult = 
  []( double x, double y){ return x*y; };

现在我想得到一个常数 y 的单变量函数。我已经写了下面的代码,但是它不起作用。

std::function<
  std::function< double(double) > (
    std::function< double(double, double) >,
    double
  )
> funcYgivenX =
  [](std::function< double(double, double) > func2d, double inX) {
    return [&func2d, &inX]( double inY ) {
      return func2d(inX, inY);
    };
  };

我在这里做错了什么?最好(最有效)的方法是什么?

【问题讨论】:

  • 关于“我在这里做错了什么”,您能否更清楚了解您要做什么。第二个 sn-p 有很多语法错误,似乎没有意义。关于“最有效”和“最佳”,即使对于一些明确定义的目标,这些判断也取决于很多事情。您可以从定义您的标准开始。非常明确。
  • 您在寻找std::bind吗?
  • @T.C.在 C++11 中,bind 只需要在几个极端情况下使用,而在 C++14 中,即使在这些情况下,绑定问题也可以使用 lambdas 来解决。
  • @SU3 我猜你在 STL 的“从不使用bind”阵营。我在这个问题上比较矛盾。

标签: c++ c++11 lambda


【解决方案1】:

在 C++11 中,std::bind 在引入 lambdas 后基本上已经过时。 这是使用 lambda 进行绑定的示例。

int add(int a, int b) {return a + b;}

int main() {
  auto add2 = [](int a){ return add(a,2); };

  std::cout << add2(3) << std::endl; // prints 5
}

有关 lambda 优于 std::bind 的参考,可以阅读 Scott Meyers 的 Effective Modern C++,第 32 条。在 C++11 中,不可能使用 lambda 移动捕获,而这只能用std::bind 模拟行为。在 C++14 中引入了对 lambdas 的初始化捕获,即使是这种极端情况也可以用 lambdas 很好地解决。

【讨论】:

    【解决方案2】:

    不不不。 这就是std::bind 存在的目的!

    #include <iostream>
    #include <functional>
    #include <typeinfo>
    
    int add(int a, int b) {return a + b;}
    
    int main() {
    
        //I believe this here is just a special type of bound function.
        auto add2 = std::bind(add, std::placeholders::_1, 2);
    
        //Yup.
        std::cout << typeid(add2).name() << std::endl;
        //Here's the type of the first function.
        std::cout << typeid(add).name() << std::endl;
        //The new function.
        std::cout << add2(1) <<  std::endl;
    
        return 0;   
    }
    

    http://coliru.stacked-crooked.com/a/56c0459617ba61d5

    您所做的只是提供函数,然后将要提供给新函数的参数作为std::bind 的辅助参数,并将任何“空格”替换为`std::placeholders::_X",其中X 是第 n 个参数编号,第一个参数从 1 开始。

    你甚至可以实现闭包!

    http://coliru.stacked-crooked.com/a/0b43fe3d1651fe36

    #include <iostream>
    #include <functional>
    #include <typeinfo>
    #include <vector>
    
    int add(int a, int b) {return a + b;}
    
    int main() {
    
        std::vector<std::function<int(int)>> funcs;
    
        for (int i = 0; i < 5; ++i) {
            auto f = std::bind(add, std::placeholders::_1, i+1);
            funcs.push_back(f);
        }
    
    
        int i = 0;
        for (int k = 0; k > -10; k = k - 2) {
            std::cout << funcs[i](k) << std::endl;
            ++i;
        }
    
        return 0;   
    }
    

    如果你愿意,你也可以使用 lambdas;它们也往往更干净,您可能可以避免std::bind 的讨厌类型:

    http://coliru.stacked-crooked.com/a/9df068bc41beb8ea

    #include <iostream>
    #include <functional>
    #include <typeinfo>
    #include <vector>
    
    int add(int a, int b) {return a + b;}
    
    int main() {
    
        std::vector<std::function<int(int)>> funcs;
    
        for (int i = 0; i < 5; ++i) {
            auto f = [i](int j) {return(i + j);};
            funcs.push_back(f);
        }
    
    
        int i = 0;
        for (int k = 0; k > -10; k = k - 2) {
            std::cout << funcs[i](k) << std::endl;
            ++i;
        }
    
        return 0;   
    }
    

    【讨论】:

      【解决方案3】:

      当你使用时

      return [&func2d, &inX]( double inY ){ return func2d(inX, inY); };
      

      您正在通过引用捕获变量func2dinX,当函数funcYgivenX 返回时,它们将成为悬空引用。

      改为按值捕获的函数。

      std::function< std::function< double(double) >(std::function< double(double, double) >, double) > funcYgivenX
        = [](std::function< double(double, double) > func2d, double inX)
        {
          return [func2d, inX]( double inY ){ return func2d(inX, inY); };
        };
      

      当你使用

      return [func2d, inX]( double inY ){ return func2d(inX, inY); };
      

      您正在按值捕获变量func2dinX,即使在函数funcYgivenX 返回后它们仍然有效。

      这就是为什么第二个版本有效,而第一个版本导致未定义的行为。

      查看工作代码:http://ideone.com/X1CRGC

      【讨论】:

      • 原文通过引用捕获两个按值函数参数;一旦函数返回,所述引用就会变得悬空。
      • @T.C.,现在说得通了。他将两个参数按值传递给funcYgivenX
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-17
      • 2021-03-16
      • 2018-09-12
      • 2013-04-25
      • 2018-09-14
      • 2020-01-24
      • 1970-01-01
      相关资源
      最近更新 更多