C++ 在C+中使用一个参数+;功能

C++ 在C+中使用一个参数+;功能,c++,c++11,reference,C++,C++11,Reference,我正在向一个由第三方库定义的类添加一个helper函数。此函数用于在位修改对象。如果我将函数作为方法编写,它将如下所示: myObject &doTheThing( int someParameter ) { myProperty = someParameter; return *this; } 所以它可以被称为: myInstance = instance1 + instance2; myInstance.doTheThing( ); 或 这很好用。但是因为我不能扩

我正在向一个由第三方库定义的类添加一个helper函数。此函数用于在位修改对象。如果我将函数作为方法编写,它将如下所示:

myObject &doTheThing( int someParameter ) {
    myProperty = someParameter;
    return *this;
}
所以它可以被称为:

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

这很好用。但是因为我不能扩展这个对象,所以我把它写成一个辅助函数

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

这在第二个用例中无效,因为临时引用不能绑定到非常量引用(人为的测试用例:)。有没有一种不使用“按副本传递”的方法来解决这个问题?如果复制是唯一的方法,是否有一些最佳实践方法可以帮助编译器正确地优化它?(C++11很好)

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

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

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

您可以使用c++11
右值函数来解决这个问题

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
ref的函数

例如:

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;
}
一切都应该很好。由于C++11,如果从临时对象创建,对象将简单地移动到参数列表中。这同样适用于返回值。在您的情况下,编译器甚至可能选择省略move构造,并根据调用的上下文一起避免这两个移动

这改变了
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
将导致未定义的行为

请编写
myObject&doTheThing
(即将
&
绑定到返回类型,而不是函数),您的布局非常不统一。您希望它做什么?完成
显示后,更新
myProperty
会发生什么情况(因此,临时对象会消失)?您的函数将返回一个引用,而不管您将符号放在何处。我认为重点是,在类型旁边看到它更常见(因此更明显)。举个例子,我错误地认为它按值返回了一个myObject,直到我看到右边的符号和。@Joel,TemplateRex:我从定义变量中继承了一个习惯,我总是将
*
&
与变量放在一起,以防止出现
int*a,b
@matstpeterson我希望它对传递给display的
instance1+instance2
的结果进行一些更改,display将其作为常量引用,并使用它执行一些与显示相关的操作。之后,它被丢弃。我应该提到,
display
采用了const引用。我对此有所了解,但这是一个有点复杂的设计(可能是因为希望返回引用的函数可以用右值调用)。无论如何,通过将调用转发到左值版本,您可以跳过采用右值引用的重载的实现:
T&do(T&&obj,int p){return do(obj,p);}
根据您的问题,您想要接受一个r值。几乎只有两种方法可以做到这一点:按r值引用传递或按副本传递。如果不是这样,你在寻找什么?@DavidRodríguez dribeas这是正确的,对于这样一件简单的事情来说有点复杂。但它允许保持
旁路ref
的优化。我跳过重载实现编辑了我的文章。谢谢,这就是我要找的(我更新的测试:)正如Ralph Tandetzky在回答中指出的,也许你不应该返回第二个重载的引用(使用右值ref参数),比如
myObject doTheThing(myObject&&object,int-someParameter)
。这将有助于正确处理返回值,并且对于生存期问题不太容易出错。与Pierre的回答中的注释相同,您不需要实现第二个重载,只需转发到第一个重载即可。再说一次,我对函数返回对右值对象的左值引用的设计不满意…@DavidRodríguez dribeas,我同意这种转发。至于设计,我想不出其他方式来尊重OP的愿望。它可能不是一个g
auto && obj = doTheThing( createTemporary(), 5 );
doSomethingElse( obj );