在过去一天左右,我一直在学习移动构造函数,试图遵循大多数人似乎建议的通过价值返回的一般规则,并且遇到了一个有趣的(对我)困境。
I have been learning about move constructors over the last day or so, trying to stick to a general rule of returning by value as most people seem to suggest, and have come across an interesting (to me) dilemma.
假设我有一个昂贵的构造/复制类'C',它已经正确定义了复制构造函数,赋值运算符,移动构造函数和移动赋值运算符。
Assume that I have an expensive to construct/copy class 'C' that has correctly defined copy constructor, assignment operator, move constructor and move assignment operator.
首先,这段代码像我预期的那样消除了复制构造函数:
First, this piece of code elides the copy constructor as I expected:
C make_c1() { return C(); }:
C make_c2() { C tmp; return tmp; }这样做(无论我传入1还是2):
and so does this (whether I pass in a 1 or 2):
C make_c3(int a) { return a == 1 ? make_c1() : make_c2(); }这是当我遇到一个问题:
It's when I get to this that I have an issue:
C make_c4(int a) { C tmp; return a == 1 ? make_c1() : tmp; }传入1触发RVO获取make_c1的结果, 2触发tmp上的复制构造函数。
Passing in a 1 triggers RVO for the result of make_c1, but passing in a 2 triggers the copy constructor on tmp.
将函数修改为以下内容会导致为tmp触发move构造函数:
Amending the function to the following causes the move constructor to be triggered for tmp instead:
C make_c5(int a) { C tmp; return a == 1 ? make_c1() : std::move(tmp); }除了...之外,所有伟大和美好的
All great and wonderful except...
在这些简单的例子中,RVO被触发的程度与我所希望的一样。
In these simple examples, RVO has been triggered pretty much as I'd hoped.
但是如果我的代码稍微复杂一些一些编译器不会在最后一个函数中唤起RVO?在这种情况下,我需要在std :: move中调用make_c1,这将使代码在那些能唤起RVO的编译器上效率较低。
However what if my code is slightly more complex and on some compilers doesn't evoke RVO in that last function? In that case, I'd need to wrap my call to make_c1 in std::move, which will make the code less efficient on those compilers that do evoke RVO.
所以我的问题是:
我一直在玩的编译器是Cygwin上的GCC 4.5.3。
The compiler I have been playing with is GCC 4.5.3 on Cygwin.
推荐答案隐含的回归移动在RVO合法的相同上下文中是合法的。当表达式是具有与函数返回类型([class.copy] / p31 / b1)相同的cv-unalalified类型的非易失性自动对象(除了函数或catch子句参数之外)的名称时,RVO是合法的)。
The implicit move-on-return is only legal in the same contexts in which RVO is legal. And RVO is legal when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv- unqualified type as the function return type ([class.copy]/p31/b1).
如果将 make_c4 变换为:
C make_c4(int a) { C tmp; if (a == 1) return make_c1(); return tmp; }然后你会得到 make_c4(2)。
更新:您的 make_c5
我应该还包括对[expr.cond] / p6 / b1的引用,它解释了第二个表达式是prvalue时的条件表达式的语义,第三个是一个左值,但两者都具有相同的类型:
I should have also included a reference to [expr.cond]/p6/b1 which explains the semantics of the conditional expression when the second expression is a prvalue and the third is an lvalue, but both have the same type:
第二个和第三个操作数具有相同的类型;结果是的类型。如果操作数具有类类型,结果是结果类型的prvalue 临时,它是从第二个操作数或第三个操作数复制初始化的,取决于的值第一个操作数。
The second and third operands have the same type; the result is of that type. If the operands have class type, the result is a prvalue temporary of the result type, which is copy-initialized from either the second operand or the third operand depending on the value of the first operand.
这一段指定了条件的结果prvalue是从你的示例中的第三个参数的复制初始化。在[dcl.init] / p14中定义了复制初始化。当副本初始化的源是类类型的左值时,这将调用类型的副本构造函数。如果源是一个右值,它将调用move构造函数,如果存在,否则它将调用复制构造函数。
I.e. this paragraph specifies that the resultant prvalue of the conditional is copy-initialized, from the 3rd argument in your example. Copy-initialization is defined in [dcl.init]/p14. When the source of a copy-initialization is a class-type lvalue, this will invoke the type's copy constructor. If the source is an rvalue, it will invoke the move constructor if one exists, else it will invoke the copy constructor.
条件表达式的规定不允许即从左值参数的隐式移动,即使条件表达式是返回表达式的一部分。这种语言可能是为了允许这样一个隐含的移动,但据我所知,它从来没有被提出过。此外,条件表达式的现有规范已经非常复杂,使得对语言的这种改变变得更加困难。
The specification of the conditional expression has no allowance for an implicit move from an lvalue argument, even if the conditional expression is part of a return expression. It is possible that the language could have been crafted to allow such an implicit move, but as far as I know, it was never proposed. Furthermore the existing specification of the conditional expression is already extremely complicated, making such change to the language all the more difficult.
更多推荐
RVO,移动操作和困境
发布评论