2016-10-24 6 views
5

次のコードでエラーが発生する理由を理解できますか?関数オブジェクトをラップするためにstd ::関数を使用する

class A 
{ 
    public: 
    float& operator()() 
    { 
    return _f; 
    } 

    private: 
    float _f = 1; 
} a; 


auto& foo() 
{ 
    std::function<float()> func = a; 
    return func(); 
} 

int main() 
{ 
    std::cout << foo() << std::endl; 
} 

エラー:

ここ
error: non-const lvalue reference to type 'float' cannot bind to a temporary of type 'float' 
    return func(); 
     ^~~~~~ 
1 error generated. 

operator()に、私は_fへの参照を返し、その結果、私はfunc()が一時的なものではないと思いました。 誰かが私の理解を助けてくれれば幸いです。

答えて

1

std::function<float()> funcの場合、float&ではなく、floatを返すファンクタとして、funcと宣言しています。エラーメッセージが示すように、floatfunc()によって返された場合、非const左辺参照にバインドすることはできません。

上記の宣言は、ラップされているA::operator()の署名と一致しません。しかし、タイプをstd::function<float&()> funcに変更して、署名がA::operator()に一致すると、コンパイルエラーが混乱する可能性がありますが、UBにつながるローカル変数にバインドされた参照を返します。

std::function<float()> func = a;の場合、std::functionaのコピーで初期化されます。 func()は、ローカル変数であるfuncにラップされたAのメンバーにバインドされた参照を返します。そして、参照が関数fooから出るときにつまずくでしょう。

あなたの設計によっては、auto& foo()auto foo()に変更します。つまり、コピーで戻り値を渡すと、ここでUBを回避できます。

+0

正しくコンパイルされていると思いますが、正しく実行されていない(たとえば、クラッシュしたり、0が表示されている)ため、UBだと恐れています。 –

+0

私はこの質問が面白いと感じました。もしそれを 'auto&'と ''にするとうまくいきます。 'auto'と' 'とすると動作します。どちらが正しいか?私は 'auto'と' 'がローカル変数を参照渡しで返すべきではないので正しいと思います。 –

+0

@MarsonMaoはい、私たちはローカルにバインドされた参照を返すべきではありません、ポイントは 'foo'がコピーによってここに戻るべきです。 (なぜOPが参照によって返される必要があるのか​​はまだ分かりません) – songyuanyao

5

std::functionの問題ではなく、floatfunc()から参考にして返すようにしています。 ステートメントが終了するとすぐにオブジェクトが存在しなくなるため、これは機能しません。

auto& foo()auto foo()に変更するとうまくいくはずです。

+0

あなたは ''変わらずのままにした場合、その理由それはコンパイルできますか? 'A'の演算子が' float& 'を返すのではないのですか? –

+1

'float 'から新しい' float'をコピーすることができるためです。 – Kiskae

+0

ああ、私はそれを理解していると思う、ありがとう! –

3

ローカル変数への参照を戻すことは、変数が有効範​​囲外になると無効になることを理解していると思います。あなたが紛失しているように見えるのは、std::function<float()> func = a;は実際にaからローカルstd::functionを作成するということです。どんな意味でもaを指さない場合、funcAです。つまり、func();を呼び出すと、実際にa.operator()が呼び出されるのではなく、Afuncになります。次に、参照が悪い部分を返すローカル変数に戻ります。

コンパイルするには、テンプレートの署名をfloat&()に変更できますが、未定義の動作です。

解決方法は、参照を削除する代わりに、戻り値の型をコピー(auto)に変更することです。

+0

なぜ 'std :: function func = a;'が問題にならないのですか? 'A'の演算子の署名は'浮動 'ではありませんか?彼らは互換性があるからですか? –

+0

@MarsonMaoそれは問題を引き起こします。これは 'float&()'に変更することで修正できるコンパイルの問題です。しかし、これは依然としてUBを引き起こします。だから私たちは参考にして返すことができません。 –

+0

'auto foo()'と 'std :: function func = a;'でコンパイルして実行することができます。私はこれが面白いと感じました。理由を説明してください。 (VS2015を使用しています) –

1

上記の大きな答えを読んだ後、私はいくつかの考えを出そうとしました。

私はOPが本当に特定のオブジェクトのfloat&(OPの例ではa)を返したいと思います。

OPは(float&する必要があります)auto&を返すようにfooを望んでいるのであれば、それは以下のようにする必要があり、std::bind一部の点に注意してください。

namespace T1 
{ 
class A 
{ 
public: 
    float& operator()() 
    { 
     std::cout << "a add = " << this << std::endl; 
     return _f; 
    } 

    float getF() { return _f; } 

private: 
    float _f = 1; 
} a; 

auto& foo() 
{ 
    std::function<float&()> func = std::bind(&A::operator(), &a); 
    return func(); 
} 
} // end of namespace T1 

int main() 
{ 
    std::cout << "global a add = " << &(T1::a) << std::endl; // check a's address 
    float& f = T1::foo(); // note that `a`'s address is the same 
    std::cout << f << std::endl; // still 1 
    f = 777; 
    std::cout << f << std::endl; // now 777 
    std::cout << T1::a.getF() << std::endl; // it's 777 

    return 0; 
} 
関連する問題