对右值引用的一些澄清 [英] Some clarification on rvalue references

查看:121
本文介绍了对右值引用的一些澄清的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

第一: std :: move std :: forward 定义在哪里?我知道他们做什么,但我找不到任何标准头需要包括他们的证据。在gcc44中有时 std :: move 是可用的,有时它不是,因此一个确定的include指令将是有用的。

First: where are std::move and std::forward defined? I know what they do, but I can't find proof that any standard header is required to include them. In gcc44 sometimes std::move is available, and sometimes its not, so a definitive include directive would be useful.

当实现移动语义时,源大概处于未定义状态。这个状态是否必须是对象的有效状态?显然,你需要能够调用对象的析构函数,并且能够通过类暴露的任何方法来赋值给它。但是其他操作是否有效?我想我的问题是,如果你的类保证某些不变式,你应该努力执行这些不变式,当用户说他们不在乎他们了吗?

When implementing move semantics, the source is presumably left in an undefined state. Should this state necessarily be a valid state for the object? Obviously, you need to be able to call the object's destructor, and be able to assign to it by whatever means the class exposes. But should other operations be valid? I suppose what I'm asking is, if your class guarantees certain invariants, should you strive to enforce those invariants when the user has said they don't care about them anymore?

下一步:当你不关心移动语义,是否有任何限制会导致非const引用在处理函数参数时优先于右值引用? void function(T&); over void function(T&传递函数临时值是偶尔有用的,所以似乎只要可行,就应该授予该选项。和右值引用本身是左值,所以你不能无意地调用移动构造函数而不是复制构造函数,或类似的东西。我没有看到一个缺点,但我确定有一个。

Next: when you don't care about move semantics, are there any limitations that would cause a non-const reference to be preferred over an rvalue reference when dealing with function parameters? void function(T&); over void function(T&&); From a caller's perspective, being able to pass functions temporary values is occasionally useful, so it seems as though one should grant that option whenever it is feasible to do so. And rvalue references are themselves lvalues, so you can't inadvertently call a move-constructor instead of a copy-constructor, or something like that. I don't see a downside, but I'm sure there is one.

这给我的最后一个问题。您仍然不能将临时绑定到非const引用。但是你可以将它们绑定到非const值引用。然后你可以将该引用作为另一个函数中的非const引用传递。

Which brings me to my final question. You still can not bind temporaries to non-const references. But you can bind them to non-const rvalue references. And you can then pass along that reference as a non-const reference in another function.

void function1(int& r) { r++; }
void function2(int&& r) { function1(r); }
int main() { 
    function1(5); //bad
    function2(5); //good
}

除了它不做任何事情,任何错误的代码?我的直觉当然不是,因为改变右值引用是对他们的存在的整体点。如果传递的值是合法的const,编译器会抓住它,并大声喊叫你。但是,在所有的情况下,这是一个机制的逃避,可能是因为一个原因,所以我只是想确认,我不做任何愚蠢的。

Besides the fact that it doesn't do anything, is there anything wrong with that code? My gut says of course not, since changing rvalue references is kind of the whole point to their existence. And if the passed value is legitimately const, the compiler will catch it and yell at you. But by all appearances, this is a runaround of a mechanism that was presumably put in place for a reason, so I'd just like confirmation that I'm not doing anything foolish.

推荐答案


首先:std :: move和std :: forward定义在哪里?

First: where are std::move and std::forward defined?

请参阅 20.3 实用程序组件< utility>

See 20.3 Utility components, <utility>.


实施移动语义时,源大概会处于未定义状态。这个状态是否必须是对象的有效状态?

When implementing move semantics, the source is presumably left in an undefined state. Should this state necessarily be a valid state for the object?

显然,对象应该是可破坏的。但是,除此之外,我认为这是一个好主意,仍然是可分配的。标准对于满足MoveConstructible和MoveAssignable的对象表示:

Obviously, the object should still be destructibly. But further than that, I think it's a good idea to be still assignable. The Standard says for objects that satisfy "MoveConstructible" and "MoveAssignable":


[注意:rv仍然是一个有效的对象。它的状态是未指定的。 - end note]

[ Note: rv remains a valid object. Its state is unspecified. — end note ]

这意味着,我认为对象仍然可以参与任何操作, 。这包括CopyConstructible,CopyAssignable,Destructible等东西。注意,从核心语言的角度来看,这不需要任何对你自己的对象。只有在您触及标识库组件并声明这些要求时,才需要执行这些要求。

This would mean, I think, that the object can still participate in any operation that doesn't state any precondition. This includes CopyConstructible, CopyAssignable, Destructible and other things. Notice that this won't require anything for your own objects from a core language perspective. The requirements only take place once you touch Standard library components that state these requirements.


下一页:当你不关心移动语义时,是否有任何限制会导致非const引用在处理函数参数时优先于右值引用?

Next: when you don't care about move semantics, are there any limitations that would cause a non-const reference to be preferred over an rvalue reference when dealing with function parameters?

不幸的是,这很重要的取决于参数是否在函数模板中并且使用模板参数:

This, unfortunately, crucially depends on whether the parameter is in a function template and uses a template parameter:

void f(int const&); // takes all lvalues and const rvalues
void f(int&&); // can only accept nonconst rvalues

但是对于函数模板

template<typename T> void f(T const&);
template<typename T> void f(T&&);

你不能这么说,因为第二个模板在用一个左值调用后,作为合成声明的参数,用于非条件左值的类型 U& (并且是更好的匹配)和 U const& 对于const lvalues(并且是模糊的)。 据我所知,没有部分排序规则来消除第二个歧义。然而,这已知

You can't say that, because the second template will, after being called with an lvalue, have as parameter of the synthesized declaration the type U& for nonconst lvalues (and be a better match), and U const& for const lvalues (and be ambiguous). To my knowledge, there is no partial ordering rule to disambiguate that second ambiguity. However, this is already known.

- 编辑 - / strong>

-- Edit --

尽管有问题报告,我不认为这两个模板是模糊的。部分排序将使第一个模板更专业,因为在取走引用修饰符和 const 之后,我们会发现两个类型是相同的,然后注意到第一个模板有一个引用const。标准说( 14.9.2.4

Despite that issue report, I don't think that the two templates are ambiguous. Partial ordering will make the first template more specialized, because after taking away the reference modifiers and the const, we will find that both types are the same, and then notice that the first template had a reference to const. The Standard says (14.9.2.4)


推导在两个方向上成功(即,上述变换之后的类型是相同的),并且如果来自参数模板的类型比来自参数模板的类型(如上所述)更加cv-qualaled,则该类型被认为是比其他更专业。

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transfor-mations above) and if the type from the argument template is more cv-qualified than the type from the parameter template (as described above) that type is considered to be more specialized than the other.

如果对于每个类型被考虑,给定的模板至少与所有类型专用,并且更专门用于一些类型的类型,而另一个模板不是更专用于任何类型或至少不是对任何类型专门化,则给定的模板比其他模板更专业。

If for each type being considered a given template is at least as specialized for all types and more specialized for some set of types and the other template is not more specialized for any types or is not at least as specialized for any types, then the given template is more specialized than the other template.

这使得 T const& 模板成为部分排序的胜者

This makes the T const& template the winner of partial ordering (and GCC is indeed correct to choose it).

- 编辑结束 -

-- Edit End --


我的最后一个问题。您仍然不能将临时绑定到非const引用。但是你可以将它们绑定到非const值引用。

Which brings me to my final question. You still can not bind temporaries to non-const references. But you can bind them to non-const rvalue references.

这在本文。使用 function2 的第二个调用仅使用非循环右值。程序的其余部分不会注意到如果它们被修改,因为他们将不能再访问那些右值!而你传递的 5 不是类类型,因此创建了一个隐藏的临时,然后传递给 int&& 右值引用。调用 function2 的代码将无法访问此隐藏对象,因此不会注意到任何更改。

This is nicely explained in this article. The second call using function2 only takes nonconst rvalues. The rest of the program won't notice if they are modified, because they won't be able to access those rvalues afterwards anymore! And the 5 you pass is not a class type, so a hidden temporary is created and then passed to the int&& rvalue reference. The code calling function2 won't be able to access that hidden object here, so it won't notice any change.

不同的情况是,如果你这样做:

A different situation is if you do this one:

SomeComplexObject o;
function2(move(o));

您已明确要求 o ,因此将根据其移动规范进行修改。但是,移动是一种逻辑上不修改的操作(参见文章)。这意味着你是否移动不应该从调用代码观察到:

You have explicitly requested that o is moved, so it will be modified according to its move specification. However moving is a logically non-modifying operation (see the article). This means whether you move or not shouldn't be observable from the calling code:

SomeComplexObject o;
moveit(o); // #1
o = foo;

如果删除移动的行,行为将仍然是相同的,因为它已被覆盖。然而,这意味着在移动之后使用 o 的值的代码是 bad ,因为它破坏了 moveit 和调用代码。因此,该标准没有规定从集装箱移出的具体值。

If you erase the line that moves, behavior will still be the same, because it's overwritten anyway. This however means that code that uses the value of o after it has been moved from is bad, because it breaks this implicit contract between moveit and the calling code. Thus, the Standard makes no specification about the concrete value of a moved from container.

这篇关于对右值引用的一些澄清的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆