使用 std::function 包装函数对象
Using an std::function for wrapping a 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()
中,我 return 引用了 _f
因此,我认为 func()
不是临时的。
如果有人能帮我理解就太好了。
问题不在于 std::function
的使用,而是您试图 return 来自 func()
的临时 float
作为参考。
这将不起作用,因为该对象将在语句结束后立即不复存在。
如果您将 auto& foo()
更改为 auto foo()
,它应该可以工作。
对于 std::function<float()> func
,您将 func
声明为函子 returning float
,而不是 float&
。如错误消息所述,由 func()
编辑的临时 float
return 不能绑定到非常量左值引用。
以上声明与被包装的 A::operator()
的签名不匹配。但请注意,如果将类型更改为 std::function<float&()> func
以匹配 A::operator()
的签名,编译错误可能会得到解决,但随后我们会将 return 引用绑定到局部变量,这会导致到 UB。
请注意,对于 std::function<float()> func = a;
,std::function 使用 a
的副本进行初始化。然后 func()
将 return 引用绑定到 A
的成员,包裹在 func
中,这是一个局部变量。并且当退出函数 foo
.
时引用将悬空
如何修复它取决于您的设计,将 auto& foo()
更改为 auto foo()
,即通过复制传递 return 值将避免此处的 UB。
我想您明白,一旦变量超出范围,return对局部变量的引用就无效了。不过,您似乎缺少的是 std::function<float()> func = a;
实际上从 a
创建了一个本地 std::function
。它不以任何方式指向 a
,func
有它自己的 A
。这意味着调用 func();
实际上并不调用 a.operator()
而是调用 func
的 A
。然后我们回到局部变量return引用是邪恶的部分。
要使其编译,您可以将模板签名更改为 float&()
,但它仍然是未定义的行为。
解决方法是将 return 类型改为副本(改为 auto
),删除引用。
看完上面的精彩回答后,我尝试给出一些不同的想法。
我猜 OP 真的想要 return 某个对象的 float&
(在 OP 的例子中是 a
)。
所以如果OP想要foo
到returnauto&
(应该是float&
),那么应该是下面这样,注意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;
}
有人可以帮助我理解为什么以下代码会导致错误吗?
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()
中,我 return 引用了 _f
因此,我认为 func()
不是临时的。
如果有人能帮我理解就太好了。
问题不在于 std::function
的使用,而是您试图 return 来自 func()
的临时 float
作为参考。
这将不起作用,因为该对象将在语句结束后立即不复存在。
如果您将 auto& foo()
更改为 auto foo()
,它应该可以工作。
对于 std::function<float()> func
,您将 func
声明为函子 returning float
,而不是 float&
。如错误消息所述,由 func()
编辑的临时 float
return 不能绑定到非常量左值引用。
以上声明与被包装的 A::operator()
的签名不匹配。但请注意,如果将类型更改为 std::function<float&()> func
以匹配 A::operator()
的签名,编译错误可能会得到解决,但随后我们会将 return 引用绑定到局部变量,这会导致到 UB。
请注意,对于 std::function<float()> func = a;
,std::function 使用 a
的副本进行初始化。然后 func()
将 return 引用绑定到 A
的成员,包裹在 func
中,这是一个局部变量。并且当退出函数 foo
.
如何修复它取决于您的设计,将 auto& foo()
更改为 auto foo()
,即通过复制传递 return 值将避免此处的 UB。
我想您明白,一旦变量超出范围,return对局部变量的引用就无效了。不过,您似乎缺少的是 std::function<float()> func = a;
实际上从 a
创建了一个本地 std::function
。它不以任何方式指向 a
,func
有它自己的 A
。这意味着调用 func();
实际上并不调用 a.operator()
而是调用 func
的 A
。然后我们回到局部变量return引用是邪恶的部分。
要使其编译,您可以将模板签名更改为 float&()
,但它仍然是未定义的行为。
解决方法是将 return 类型改为副本(改为 auto
),删除引用。
看完上面的精彩回答后,我尝试给出一些不同的想法。
我猜 OP 真的想要 return 某个对象的 float&
(在 OP 的例子中是 a
)。
所以如果OP想要foo
到returnauto&
(应该是float&
),那么应该是下面这样,注意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;
}