在C ++函数中就地处理参数

时间:2013-07-22 12:42:56

标签: c++ c++11 reference

我正在向由第三方库定义的类添加辅助函数。此函数就地修改对象。如果我将函数编写为方法,它将如下所示:

myObject &doTheThing( int someParameter ) {
    myProperty = someParameter;
    return *this;
}

所以它可以被称为:

myInstance = instance1 + instance2;
myInstance.doTheThing( );

display( (instance1 + instance2).doTheThing( ) );

哪个工作正常。但由于我无法扩展该对象,我将其写为辅助函数;

myObject &doTheThing( myObject &object, int someParameter ) {
    object.myProperty = someParameter;
    return object;
}

在第二个用例中无效,因为临时无法绑定到非const引用(人为的测试用例:http://codepad.org/5frJjCUb)。有没有办法解决这个问题而不使用pass-by-copy?如果复制是唯一的方法,是否有一些最佳实践方法可以帮助编译器正确地进行优化? (C ++ 11很好)

3 个答案:

答案 0 :(得分:2)

您可以使用c ++ 11 rvalue来解决此问题。

  

r值参考参数的目的是具体检测对象是否为r值。因为如果一个对象是一个r值,那么该函数知道它不会再被使用,所以它可以用它做任何想做的事。

您可以重载该功能:

myObject &doTheThing( myObject &object, int someParameter ) {
    object.myProperty = someParameter;
    return object;
}

myObject &doTheThing( myObject &&object, int someParameter ) {
    //                         ^^
    object.myProperty = someParameter;
    return object;
}

保留您的实现,只需添加rvalue的实现,因为当我在开始时讨论的检测没有发生时,您必须有一个lvalue引用的函数。 / p>

例如:

doTheThing( instance1 + instance2, 1 ) // uses the rvalue implementation
doTheThing( myInstance, 1);            // uses the lvalue implementation

编辑: 正如在评论中所说,你可以跳过重载实现,但这会导致最后的问题:使用rvalue引用似乎可以解决问题但是如果我们进一步看看:临时的使用(instance1 + instance2)创建的objet将在执行doTheThing后销毁。解决方案可能是:

myObject &doTheThing( myObject &object, int someParameter ) {
    object.myProperty = someParameter;
    return object;
}

myObject doTheThing( myObject &&object, int someParameter ) {
    //  ^
    object.myProperty = someParameter;
    return object;
}

重载值的返回将避免一些生命问题。

但是你现在不能跳过重载实现,因为编译器会生成这个错误:

error C2664: 'concat' : cannot convert parameter 1 from 'MyObj' to 'MyObj &&'
    You cannot bind an lvalue to an rvalue reference

答案 1 :(得分:1)

您可以重载辅助函数以获取右值引用,这将在第二种情况下起作用:

myObject &doTheThing( myObject &&object, int someParameter ) {
    object.myProperty = someParameter;
    return object;
}

请注意,您需要两个版本的函数:一个参考,一个参考右值。

答案 2 :(得分:0)

按值获取参数并按值返回

将您的代码更改为

myObject doTheThing( myObject object, int someParameter ) {
    object.myProperty = someParameter;
    return object;
}

一切都应该正常工作。从C ++ 11开始,如果从临时文件创建,对象将被简单地移动到参数列表中。这同样适用于返回值。在您的情况下,编译器甚至可以选择忽略移动构造并根据调用的上下文避免两个移动。

这显然改变了doTheThing(bla, blub)变化的含义。如果要使用此函数修改对象,则必须编写

bla = doTheThing( bla, blub );

代替。这看起来不太好,但提供了值得理解的值语义。但它解决了你的问题,没有以下陷阱。

R值参考不是解决方案

乍一看似乎你可以用右值参考来解决这个问题,但结果证明这是不安全的。如果你定义了两个函数

myObject &  doTheThing( myObject &  object, int someParameter );
myObject && doTheThing( myObject && object, int someParameter );

然后写

auto && obj = doTheThing( createTemporary(), 5 );
doSomethingElse( obj );

会将返回引用的生命周期延长到obj的范围。但是,createTemporary()创建的对象将在第一行的末尾被销毁。因此,访问第二行中的obj将导致未定义的行为。