使用std :: function包装函数对象

时间:2016-10-24 09:08:03

标签: c++ c++14 std-function function-object

有人可以帮助我理解为什么以下代码会导致错误吗?

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()不是临时的。 如果有人帮助我理解,那就太棒了。

4 个答案:

答案 0 :(得分:5)

问题不在于使用std::function,而是您尝试从float返回临时func()作为参考。 这是行不通的,因为一旦语句结束,对象就会不复存在。

如果您将auto& foo()更改为auto foo(),则应该有效。

答案 1 :(得分:3)

我想你明白,一旦变量超出范围,返回对局部变量的引用是无效的。您似乎缺少的是Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com....../com.....activities.MainActivity}: java.lang.NullPointerException: Cannot return null from a non-@Nullable @Provides method at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3254) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3350) at android.app.ActivityThread.access$1100(ActivityThread.java:222) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1795) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:158) at android.app.ActivityThread.main(ActivityThread.java:7229) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) Caused by java.lang.NullPointerException: Cannot return null from a non-@Nullable @Provides method at dagger.internal.Preconditions.checkNotNull(Preconditions.java:48) at com.....di.modules....Module_Provides...Factory.get(...Module_Provides...Factory.java:30) at com......di.modules....Module_Provides...Factory.get(...Module_Provides...Factory.java:10) at dagger.internal.DoubleCheck.get(DoubleCheck.java:47) at com.....fragments.HomeFragment_MembersInjector.injectMembers(HomeFragment_MembersInjector.java:39) at com.....fragments.HomeFragment_MembersInjector.injectMembers(HomeFragment_MembersInjector.java:9) at com.....di.DaggerApplicationComponent.inject(DaggerApplicationComponent.java:217) at com.....fragments.HomeFragment.onCreate(HomeFragment.java:87) at android.support.v4.app.Fragment.performCreate(Fragment.java:2068) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1055) at android.support.v4.app.BackStackRecord.setLastIn(BackStackRecord.java:838) at android.support.v4.app.BackStackRecord.calculateFragments(BackStackRecord.java:878) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:719) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1677) at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:388) at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:604) at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1265) at android.app.Activity.performStart(Activity.java:6915) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3217) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3350) at android.app.ActivityThread.access$1100(ActivityThread.java:222) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1795) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:158) at android.app.ActivityThread.main(ActivityThread.java:7229) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) 实际上从std::function<float()> func = a;创建了本地std::function。它不以任何方式指向aa拥有它自己的func。这意味着调用A实际上不会调用func();,而是调用a.operator()的{​​{1}}。然后我们回到局部变量返回一个引用是邪恶的部分。

要进行编译,您可以将模板签名更改为A,但它仍然是未定义的行为。

修复方法是将返回类型更改为副本(改为func),删除引用。

答案 2 :(得分:1)

对于std::function<float()> func,您宣布func为返回float而不是float&的仿函数。正如错误消息所述,float返回的临时func()不能绑定到非常量左值引用。

上述声明与被包装的A::operator()的签名不符。但请注意,如果将类型更改为std::function<float&()> func以匹配A::operator()的签名,则可以调整编译错误,但之后我们将返回绑定到局部变量的引用,这将导致UB。 / p>

请注意,对于std::function<float()> func = a;std::function初始化为a的副本。然后func()将返回一个绑定到A func成员的引用,该成员是一个局部变量。当退出函数foo时,引用将悬挂。

如何修复它取决于您的设计,将auto& foo()更改为auto foo(),即通过副本传递返回值将避免UB。

答案 3 :(得分:1)

在阅读了上面的重要答案后,我试着提出一些不同的想法。

我想OP确实想要返回某个对象的float&(在OP的示例中为a)。

因此,如果OP希望foo返回auto&(应该是float&),那么它应如下所示,请注意std::bind部分:< / p>

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;
}