2010-04-05 29 views
10

首先:std::movestd::forward定义在哪里?我知道他们做了什么,但是我找不到任何需要包含标准标题的证据。在gcc44中有时可以使用std::move,有时它不是,所以一个明确的include指令会很有用。右值引用的一些说明

当实现移动语义时,源可能处于未定义状态。这个状态是否应该是该对象的有效状态?显然,你需要能够调用对象的析构函数,并且能够通过该类所公开的任何方式来分配它。但是其他操作应该有效吗?我想我要问的是,如果你的课程保证了某些不变量,当用户说他们不再关心它们时,你是否应该努力执行这些不变量?

下一页:当您不关心移动语义时,在处理函数参数时是否有任何限制会导致非常量引用优于右值引用? void function(T&); over void function(T&&);从调用者的角度来看,能够传递函数临时值有时是有用的,所以看起来好像应该在可行的时候授予该选项。右值引用本身就是左值,所以你不能无意中调用移动构造函数而不是复制构造函数或类似的东西。我没有看到一个缺点,但我确信有一个。

这使我想到我的最后一个问题。您仍然无法将临时对象绑定到非常量引用。但是你可以将它们绑定到非常量右值引用。然后,您可以将该引用作为另一个函数中的非const引用传递。

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

除了它没有做任何事情,这个代码有什么问题吗?我的直觉当然不是,因为改变右值引用对他们的存在来说是完整的。如果传递的值是合法的const,编译器会抓住它并向你大喊。但从各方面来看,这都是由于某种原因推测出来的一种机制的过渡,所以我只想确认我没有做任何愚蠢的事情。

回答

10

第一:在哪里std :: move和std :: forward定义了?

请参阅20.3实用部件,<utility>


当实现移动语义,源极被推测处于未定义状态。这个状态是否应该是该对象的有效状态?

很明显,该对象应该仍然是可破坏的。但除此之外,我认为这是一个好主意,仍然可以分配。该标准对满足“MoveConstructible”和“MoveAssignable”的物体说:

[注:rv仍然是一个有效的对象。它的状态是未指定的。 - 结束注释]

这意味着,我认为,该对象仍然可以参与任何没有声明任何先决条件的操作。这包括CopyConstructible,CopyAssignable,Destructible和其他东西。请注意,从核心语言的角度来看,这对你自己的对象不需要任何东西。这些要求只有在您触及标明这些要求的标准库组件时才会发生。


下一页:当你不关心移动语义,在那里与函数参数打交道时,会导致非const引用方式优于右值引用任何限制?

此,不幸的是,关键取决于参数是否是一个函数模板,并使用一个模板参数:

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&常量左值(并且不明确)。 据我所知,没有偏序排列规则来消除第二歧义。 但是,is already known

--编辑--

尽管如此问题的报告,我不认为这两个模板是不明确的。部分排序会使第一个模板更加专业化,因为在拿走参考修饰符和const之后,我们会发现两种类型都是相同的,然后注意到第一个模板有一个对const的引用。该标准说(14.9.2.4

如果,对于给定的类型,减法成功在两个方向(即,类型在上述变换之后是相同的),并且如果参数模板中的类型比参数模板中的类型更具cv合格性(如上所述),则该类型被认为比其他类型更专用。

如果对于每种类型被认为给定的模板至少对于所有类型都是专门的,对于某些类型集更专业化,而另一个模板对于任何类型都不是特定的,或者对于任何类型都不是特定的,那么给定的模板比其他模板更专业。

这使得T const&模板成为部分排序的赢家(并且GCC的确选择它是正确的)。

--编辑结束--


这让我想起我的最后一个问题。您仍然无法将临时对象绑定到非常量引用。但是你可以将它们绑定到非常量右值引用。

这在this article中有很好的解释。使用function2的第二个调用仅使用非常数右值。程序的其余部分不会注意到它们是否被修改,因为它们将不能再访问那些rvalues了!您传递的5不是类类型,因此会创建一个隐藏的临时值,然后将其传递给右值引用。调用function2的代码将无法访问该隐藏对象,因此它不会注意到任何更改。

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

SomeComplexObject o; 
function2(move(o)); 

您已明确要求o移动,所以它会根据其移动规格进行修改。但移动是逻辑上不修改操作(请参阅文章)。这意味着无论你在移动或不应该不能从调用代码观察到:

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

如果擦除移动线,行为仍然是相同的,因为它反正覆盖。但是,这意味着代码从o移出后的代码是不好,因为它打破了moveit与调用代码之间的隐式合约。因此,该标准没有规定从容器移出的具体价值。

+1

谢谢你的详细回复。我认为我理解的东西比以前少了,但是没有错。根据那篇文章,像void f(int &&);''这样的函数不能接受左值,除非它们被明确地转换了吗?这绝对不是我见过的行为,所以我认为这是gcc-4.4的一个缺点我已经做了所有的测试,以了解所有的交互方式如何都毫无价值: -/ – 2010-04-05 18:03:03

+0

@Dennis,是在前一段时间在工作草案中改变的:'int &&'不隐式绑定到 – 2010-04-05 19:10:04

+0

'这篇文章'链接已经死了,你可以把它改为当前版本吗? – 2013-07-08 12:56:02

2

包括由utility


Here是我读到右值的文章。

我不能帮你休息,对不起。

4

where std :: move和std :: forward定义了吗?

std::movestd::forward<utility>中声明。请参阅第20.3节[实用程序]开头处的简介。

当实现移动语义时,源可能处于未定义状态。

它当然取决于你如何实现move-constructor和move-assignment操作符。但是,如果要在标准容器中使用对象,则必须遵循MoveConstructibleMoveAssignable的概念,该概念表示该对象保持有效,但保留在未指定的状态,即您绝对可以销毁它。