2016-10-24 51 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,你声明func作为仿函数返回一个float,而不是一个float&。如错误消息所述,由func()返回的临时float不能绑定到非常量左值引用。

以上声明与A::operator()的包装不符。但是请注意,如果将类型更改为std::function<float&()> func以匹配A::operator()的签名,则可以避免编译错误,但是我们会返回一个绑定到局部变量的引用,从而导致UB。

请注意,对于std::function<float()> func = a;std::function初始化为副本a。然后func()将返回一个绑定到A成员的引用,包含在func中,这是一个局部变量。退出功能foo时,参考将会停顿。

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

+0

我也这么认为,因为它编译得很好,但恐怕它是UB,因为它看起来没有正确运行(例如它崩溃或打印0)。 –

+0

我发现这个问题很有趣:如果你使它成为'auto&'和'',它可以工作;如果你使它成为'auto'并且''它也可以。哪一个是正确的?我觉得'auto'和''是正确的,因为你不应该通过引用返回本地变量,对吧? –

+0

@MarsonMao是的,我们不应该返回引用绑定到本地,点'foo'应该通过复制在这里返回。 (为什么OP需要通过引用返回,这仍然是未知的。) – songyuanyao

5

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

如果你将auto& foo()更改为auto foo()它应该可以工作。

+0

如果离开了''不变,为什么它可以编译?是不是'A'的操作符返回'float'? –

+1

因为它可以从'float'复制构建一个新的'float'。 – Kiskae

+0

啊,我想我有点理解它,谢谢! –

3

我想你明白,一旦变量超出范围,返回一个局部变量的引用无效。你似乎错过的是std::function<float()> func = a;实际上从a创建本地std::function。它不以任何方式指向afunc有它自己的A。这意味着调用func();实际上不会调用a.operator(),而是funcA。然后我们回到局部变量返回一个引用是邪恶的一部分。

要编译它,可以将模板签名更改为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想foo返回auto&(这应该是一个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; 
} 
相关问题