使用std :: function和lambda参数进行分段错误

时间:2017-09-06 11:01:12

标签: c++ function c++11 lambda

你能解释为什么这段代码崩溃了吗?我希望" a"的输出,但我得到分段错误。

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


using namespace std;

struct MyStruct {
  vector<string> a;
  vector<string> b;
};

void out_data(const MyStruct& my_struct, const std::function<const vector<string>&(const MyStruct&)> getter) {
  cout << getter(my_struct)[0] << endl;
}

int main(int argc, char** argv)
{
  MyStruct my_struct;
  my_struct.a.push_back("a");
  my_struct.b.push_back("b");
  out_data(my_struct, [](const MyStruct& in) {return in.a;});
  return 0;
}

2 个答案:

答案 0 :(得分:18)

[](const MyStruct& in) {return in.a;}

lambda表达式等同于

[](const MyStruct& in) -> auto {return in.a;}

返回in.a的副本。然后,您的std::function签名会返回对本地对象的悬空引用。

lambda表达式更改为

[](const MyStruct& in) -> const auto& {return in.a;}

返回const&,修复段错误。

此外,除非您有充分理由这样做,否则请勿使用std::function传递lambdas。我建议阅读我关于这个主题的文章:"passing functions to functions"

答案 1 :(得分:2)

我责备std::function(和你)。当然,我责怪你要求std::function返回一个悬垂的参考文献,正如维托里奥罗密欧所解释的那样。但是我也责备std::function的构造函数模板,因为它没有检查这种情况,它应该在大多数情况下或者在编译时都可以检测到,并因此生成诊断。 (我使用“责备”这个词只是为了指出可能的改进方面。从这个意义上说,我也责怪自己没有考虑在我自己的unique_function类模板的实现中添加这个确切的检查。)< / p>

让我们仔细看看构造函数的签名。我为此目的选择了定义网站。

template<typename R, typename... Args>
template<typename F>
std::function<R(Args...)>::function(F f);

应该可以在这里禁止悬挂引用。当且仅当operator()是对R返回的临时引用的引用时,才会从F返回悬空引用。让我们定义(在构造函数体的范围内):

using RF = decltype(f(std::forward<Args>()...));

现在我们几乎可以肯定,如果符合function的{​​{1}},我们就会返回悬空参考:

operator()

虽然有一个问题,因为std::is_reference<R>::value && ! std::is_reference<RF>::value 可以是一个类型,其用户定义的转换运算符为RF。虽然这种转换可能仍然不安全,但我们目前没有足够的信息来决定,而且应该在一般性方面犯错。显然,我们可以检测R的目标是R的公共基类(假设上述条件为真):

RF

我只允许公开继承,因为std::is_convertible<RF *, typename std::remove_reference<R>::type *>::value 只能访问公共非模糊基类。除非有人出于某种奇怪的原因std::function std::function friend RF。 (由于转换可以在包装的函数对象内完成,因此可能不需要这样做。)

将所有内容放在一起并反转逻辑,我们可以在function构造函数的主体前加上:

using RF = decltype(f(std::forward<Args>()...));
static_assert( ! std::is_reference<R>::value  ||
                 std::is_reference<RF>::value ||
               ! std::is_convertible<RF *, typename std::remove_reference<R>::type *>::value,
               "Using this function object would result in a dangling reference in the function call" );