2017-09-06 26 views
10

您能否解释为什么此代码崩溃?我期望输出“a”,但我得到分段错误。std :: function和lambda参数的分割错误

#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; 
} 
+1

可能是因为lambda返回值*而不是引用。 –

+4

如果你看到自己曾经回过头来参考,问问自己“我指的是生命中的那个对象”?如果你不能回答,你不应该那样做。 – StoryTeller

+1

请启用警告。你的编译器可能对你尖叫,这是一个坏主意。 – Yakk

回答

16

[](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,除非你有一个很好的理由这样做,通过lambda表达式。我建议阅读我关于这个问题的文章:"passing functions to functions"

1

我责怪std::function(和你)。正如Vittorio Romeo所解释的,我当然责怪你要求std::function返回一个悬而未决的参考。但我也责怪std::function的构造函数模板不检查这种情况,它应该在编译时检测大部分或全部情况,从而生成一个诊断。 (我用“责备”一词来指出可能的改进方面,从这个意义上说,我也怪自己没有想过在我自己的unique_function类模板的实现中加入这个确切的检查。)

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

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

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

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

现在,我们几乎可以肯定的是,悬空参考将从返回的functionoperator()如果:

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 a friend of RF

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"); 
:(由于转换可在包装的函数对象内部完成,有可能是没有必要做这个)

全部放在一起,并颠倒逻辑,我们可以用前缀function构造的身体

+0

即使这不是一个答案,我赞成,因为它是有价值的信息。您是否考虑过进一步研究并撰写提案/ DR? –