将r值作为非const引用传递(VS警告C4239)

时间:2013-04-25 13:26:50

标签: c++ c++11 rvalue-reference rvalue

我想做的事情(使用C ++ lambda)是有效的:

std::vector<MyType> GetTheArray () {return something;}

const auto DoSomething = [](std::vector<MyType> & array)
{
     //Some processing that involves either sorting the 'array' or setting temporary flags on the items
};

DoSomething (GetTheArray ());

这在标准C ++中似乎是不允许的,因为rvalue不能作为非const引用传递。

我的问题:

1)有没有办法使用类型转换来执行此操作,或者我是否有义务创建一个临时变量来存储GetTheArray()的结果?

2)C ++中不允许这样做有充分的理由吗?

请注意,'GetTheArray'返回的'something'是一个即时构建的数组,而不是存储值。

5 个答案:

答案 0 :(得分:6)

从评论中可以看出,您想要的是采用矢量,破坏性地修改它(在原始状态无法重置的意义上),然后在内部使用结果。并且您希望这对于左值和右值都有效。

接下来的问题是,在左值的情况下,在函数调用完成后,保存原始容器的代码是否需要它,以及是否需要向量的原始状态。根据您的答案,您有不同的解决方案:

持有左值的来电者在通话后不再使用

(或者,持有左值的来电者需要原始状态)

这是最简单的情况。然后你的函数应该按值获取容器。如果调用者有左值,它可以std::move以避免复制(它不再关心对象)或复制,这可能更昂贵但保持原始容器不变。< / p>

如果使用 rvalue 调用该函数,则该副本将被省略或转换为廉价的隐式移动。

持有左值的来电者不需要原始状态,但需要容器

这种情况很难,你需要为同一个函数(或lambda)提供两个重载,一个使用 lvalue 来使用此调用者,另一个使用< em> rvalue-reference 用于呼叫者临时的情况。在这两种情况下,绑定都很便宜。虽然这需要更多代码,但您可以根据另一个实现一个重载:

rtype f(std::vector<Data> & );   // for lvalues
rtype f(std::vector<Data> && v)  // for rvalues
   { return f(v); }              // v is an lvalue here

你正在做lambdas的事实可能会使这个稍微复杂一些,但希望不会太多。

答案 1 :(得分:3)

如果由于某种原因你真的想修改something,你可以随时将临时文件复制到你的lambda中。

const auto DoSomething = [](std::vector<MyType> array) { /*whatever*/ }

您调用lambda编译器的方式可能会忽略副本。 C ++ 11移动语义形式化了这一点。

答案 2 :(得分:1)

(实际上,@ honk在传递价值方面打败了我。为了与众不同,我将扩展我的答案,明确地讨论使用&&

删除&,然后按值传递它。

您正在使用lambdas,因此您使用的是c ++ 11,因此您应该通过 value 传递它。是的,它会一样快。

C ++ 11非常善于注意临时被“复制”的时间。它将使用 move 替换副本。它会很快,可以说它会更容易阅读。

因此,即使您对&&std::move等等一无所知,也可以说您应该开始按价值而非参考方式传递内容。它将导致更易读的代码,如果在正确的时间使用它不会导致速度减慢。

基本上,如果将临时值(例如函数返回的值)传递给函数,则速度很快,但仅当您使用标准容器(例如vector )时才会这样。否则,您需要编写自己的&&感知构造函数,以利用可能的加速。

更新:如果您决定强制推荐它,并且/或者您使用的是自定义容器,那么您可以将&替换为&&。这将允许您传递非const引用。这称为 rvalue-reference

正如许多其他人所指出的那样,临时将在DoSomething返回后被销毁。牢记这一点。这意味着您需要在返回之前完全使用数组的内容。也许您将其打印到屏幕上,或将其存储在数据库中。或者,您可以将数据复制或移动到另一个对象中并返回该对象。

答案 3 :(得分:0)

如果您需要修改something数组,则需要返回对它的引用。否则不要通过(非const)引用传递给DoSomething 它被禁止的原因是为了避免你在这里说明的问题。您似乎要修改something,但something返回的GetTheArray()副本将被更改。这没有多大意义,因为此副本将在DoSomething退出后立即删除 你有3种方法可以解决这个问题 1.您需要修改something,然后更改GetTheArray以返回引用
2.您需要修改将被丢弃的something副本DoSomething完成它的工作,然后将DoSomething更改为按值接受数组。
3.您不需要修改something中的DoSomething,然后const引用,因为应使用DoSomething的参数。

答案 4 :(得分:0)

您正在处理从底线GetTheArray()来电中退回的临时用户。这只能绑定到const引用,然后objec的生命周期将扩展到它绑定的const引用的生命周期(因此“绑定”)。

即使你可以做你想做的事,你对DoSomething的电话仍然是一个昂贵的无操作。至少你需要return来自DoSomething的东西。

C ++禁止这样做,因为(从C ++ 11开始)它具有带有移动语义的右值引用,这可以做很棒的事情,但不能没有返回某些东西