【问题标题】:std::function accepts lambda functions with arguments of different passing type (by ref, by val)std::function 接受具有不同传递类型的参数的 lambda 函数(通过 ref,通过 val)
【发布时间】:2019-02-21 14:45:19
【问题描述】:

请看下面的代码

#include <iostream>
#include <functional>
#include <string>

int main()
{
    std::function<void(std::string&)> theFunc;
    std::string foo = "0";

    theFunc = [](std::string a) { a = "1";  };  // this compiles but has a different function signature
    theFunc(foo);
    std::cout << "foo should be 1 but is " << foo << std::endl;

    theFunc = [](auto a) { a = "2";  };         // this infers the wrong type for auto(by val not by ref), creates the wrong function signature and compiles 
    theFunc(foo);
    std::cout << "foo should be 2 but is " << foo << std::endl;

    theFunc = [](std::string& a) { a = "3";  };  // this compiles and correctly manipulates the string
    theFunc(foo);
    std::cout << "foo should be 3 and is " << foo << std::endl;

    theFunc = [](auto& a) { a = "4";  };  // this compiles and correctly manipulates the string
    theFunc(foo);
    std::cout << "foo should be 4 and is " << foo << std::endl;

    std::cin.get();
}

在代码示例中,我们为一个 std::function 分配了不同类型的 lambda。

我理解 lambda 3 因为函数签名匹配。

但是 lambda 1 创建了不同的函数签名,但编译正确。

Lambda 2 推断出错误的自动类型(通过 val 而不是通过 ref)并正确编译。

这是功能还是错误?我对函数类/lambdas 和自动类型推断有什么误解?

更新:

感谢Handy999的回答,但是为什么下面没有编译呢?

    std::function<void(std::string)> theFunc2;

    theFunc2 = [](std::string& a) { a = "1";  };  // this doesn't compile and has a different function signature
    theFunc2(foo);

【问题讨论】:

  • 第五种情况失败,因为您不能使用临时字符串调用该 lambda(即[](std::string&amp; a) { a = "1"; }("1"); 无效)。 std::function 保留该限制
  • 谢谢 Caleth,就是这样。 [](const std::string& a) { } 已编译。

标签: c++ lambda c++14 generic-lambda


【解决方案1】:

与函数指针不同,std::function 获取所有可以按指定调用的内容。如有必要,它会创建一个小型包装函数(在后台)。

所有情况下的代码

void smallWrapper(std::string& s) {
    ([](std::string a) { a = "1"; })(s);
}

void smallWrapper2(std::string& s) {
    ([](auto a) { a = "2"; })(s);
}

void smallWrapper3(std::string& s) {
    ([](std::string& a) { a = "3"; })(s);
}

void smallWrapper4(std::string& s) {
    ([](auto& a) { a = "4"; })(s);
}

可以调用。 auto 总是推导出基类型,所以总是推导出std::string。因此,案例 2=案例 1,案例 4=案例 3。这就是 std::function 所做的和应该做的。


对于第 5 种情况,确实如 Caleth 指出的那样。你不能打电话

([](std::string& a) { a = "5"; })("string");

因为您不能将引用绑定到临时对象。 (在这里,包装函数可以工作。所以,它不是一个很好的模型。)对于 const 引用,它照常工作:

([](const std::string& a) { a = "6"; })("string");

【讨论】:

  • 这不是相当正确的,因为包装器保留了参数的值类别,这就是更新案例失败的原因
  • 确实如此。价值类别保持不变。
  • 我个人认为 std::function 的这种行为极其危险。通过一个简单的拼写错误,程序会操作一个副本而不是预期的对象,而不会出现任何编译错误。
猜你喜欢
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
  • 2019-01-28
  • 1970-01-01
  • 2020-06-08
  • 1970-01-01
  • 1970-01-01
  • 2011-02-03
相关资源
最近更新 更多