2017-09-06 9 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

おそらくラムダはベクトル*を値*で返し、参照ではないからです。 –

+4

あなたが参照を返すことが自分自身を参照している場合は、自分自身に "私は人生を参照しているそのオブジェクトは何ですか?あなたが答えることができないなら、あなたはそれをすべきではありません。 – StoryTeller

+1

警告を有効にしてください。あなたのコンパイラはおそらく、これが悪い考えであるとあなたに叫んでいました。 – Yakk

答えて

16

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

ラムダ式in.aのコピーを返し

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

と等価です。 std::functionシグネチャは、ローカルオブジェクトへのダングリングリファレンスを返します。


変更ラムダ式

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

にセグメンテーション違反を固定し、代わりconst&を返します。あなたがそうする正当な理由がない限り


また、ラムダを渡すためにstd::functionを使用しないでください。件名:"passing functions to functions"に関する私の記事を読むことをお勧めします。

1

私はstd::function(あなたも)責任があります。私は、もちろん、あなたに、std::functionに、Vittorio Romeoが説明したように、ぶら下がっている参照を返すように頼んだことを非難します。しかし、私はまた、このケースをチェックしないためにstd::functionのコンストラクタテンプレートを非難します。このテンプレートはコンパイル時に検出可能なほとんどまたはすべてのケースに存在する必要があり、結果的に診断を生成します。 (私は改善の可能な領域を指摘するためだけに "責める"という言葉を使用しています。この意味で、私は自分のunique_functionクラステンプレートの実装でこの正確なチェックを追加することを考えていなかったことを自分自身に責める)。

Let'sコンストラクタの署名を詳しく見てください。私はこの目的のために定義サイトを選びました。

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

ダングリングリファレンスをここで禁止する必要があります。 RFによって返された一時的なものへの参照である場合にのみ、ぶら下がった参照がoperator()から返されます。のは、(コンストラクタ本体の範囲で)を定義してみましょう:

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

今、私たちは次の場合にダングリング参照がfunctionoperator()から返されることはほぼ確実になりますがあり

std::is_reference<R>::value && ! std::is_reference<RF>::value 

ただし、RFは、ユーザー定義の変換演算子がRのクラス型である可能性があります。この変換は依然として安全ではないかもしれませんが、私たちは決定するために十分な情報を持っておらず、一般性の面で間違いがあります。std::functionは唯一の非あいまいなパブリックアクセスすることができますので、私はここに公共の継承をのみを許可

std::is_convertible<RF *, typename std::remove_reference<R>::type *>::value 

:もちろん、我々はRの目標は、(これは上記の条件が真であると仮定している)RFの公開基底クラスであるかどうかを検出することができました基本クラス。誰かが何らかの奇妙な理由で friendRFにしていない限り。 (変換がラップ関数オブジェクトの内部で行うことができますので、これを実行する必要はおそらくありません)

が一緒にそれをすべてを置くと論理を反転、私たちはと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"); 
+0

これは答えではありませんが、私はそれが貴重な情報として賞賛されました。これをさらに研究し、提案書/ DRを書くことを検討しましたか? –

関連する問題