有人可以帮助我理解为什么以下代码会导致错误吗?
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()
不是临时的。
如果有人帮助我理解,那就太棒了。
答案 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
。它不以任何方式指向a
,a
拥有它自己的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;
}