你能解释为什么这段代码崩溃了吗?我希望" 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;
}
答案 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" );