在C++函数中的适当位置处理参数

Work on a parameter in-place in C++ function

本文关键字:位置 处理 参数 C++ 函数      更新时间:2023-10-16

我正在向由第三方库定义的类添加一个辅助函数。此函数用于在适当位置修改对象。如果我把函数作为一种方法来编写,它会是这样的:

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

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

您可以使用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参考

例如:

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

编辑:

正如评论中所说,您可以跳过重载实现,但这会导致最后一个问题:使用rvalue引用似乎可以解决问题,但如果我们进一步研究:用(instance1 + instance2)创建的临时对象将在执行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

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

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

请注意,您需要两个版本的函数:一个使用引用,另一个使用右值引用。

按值获取参数并按值返回

将代码更改为

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

一切都应该很好。由于C++11,对象将被简单地移动到您的参数列表中,如果是从临时创建的。返回值也是如此。在您的情况下,编译器甚至可以选择省略move构造,并根据调用的上下文来避免这两个移动。

这使CCD_ 7的意义发生了明显的变化。如果你想用这个函数修改一个对象,那么你必须写

bla = doTheThing( bla, blub );

相反。这看起来不太好,但提供了更好推理的价值语义。但它解决了你的问题,没有以下陷阱。

R值参考不是解决方案

乍一看,你似乎可以用右值引用来解决这个问题,但这是不安全的如果您定义的两个功能

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

然后写入

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

将把返回的引用的生存期延长到CCD_ 8的作用域。但是,createTemporary()创建的对象将在第一行的末尾被销毁。因此,访问第二行中的CCD_ 10将导致未定义的行为。