当实现移动语义时,源可能被置于未定义状态.这个状态是否一定是对象的有效状态?显然,您需要能够调用对象的析构函数,并且可以通过类暴露的任何方式赋值给它.但其他操作是否有效?我想我所要问的是,如果你的班级保证某些不变量,那么当用户说他们不在乎他们,你应该努力执行这些不变量吗?
下一个:当你不关心移动语义时,是否有任何限制,在处理函数参数时,会引起非常量引用比右值引用更好? void function(T&);过空功能(T&&);从呼叫者的角度来看,能够传递函数临时值是偶尔有用的,所以似乎应该在可行的时候授予该选项.而rvalue引用本身就是左值,所以你不能无意中调用一个移动构造函数而不是一个复制构造函数,或者类似的东西.我看不到有缺点,但我肯定有一个.
这带给我最后的问题.您仍然无法将临时文件绑定到非常量引用.但是您可以将它们绑定到非常量rvalue引用.然后,您可以将该引用作为另一个函数中的非常量引用传递.
void function1(int& r) { r++; } void function2(int&& r) { function1(r); } int main() { function1(5); //bad function2(5); //good }
除了没有做任何事情的事实外,该代码有什么问题吗?我的肠道当然不是说,因为改变价值参考是对他们存在的一个整体.如果传递的值是合法的const,编译器会抓住它并向你大喊.但是,除了所有的外表,这是一个机会的一个周期性的可能原因,因此我只是想确认我没有做任何愚蠢的事情.
解决方法
First: where are std::move and std::forward defined?
请参阅20.3实用程序组件< 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”的对象:
[ Note: rv remains a valid object. Its state is unspecified. — end note ]
这意味着我认为该对象仍然可以参与任何没有规定任何前提条件的操作.这包括CopyConstructible,CopyAssignable,Destructible等.请注意,从核心语言的角度来看,这不需要任何您自己的对象.触摸标准库组件之后,只需触发这些要求.
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 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)
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&模板部分排序的获胜者(而GCC确实是选择它).
– 编辑结束 –
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.
这在this article很好地解释了.使用function2的第二个调用只需要非常规的rvalue.程序的其余部分不会注意到它们是否被修改,因为它们将不能再访问这些值了!而你通过的5不是一个类型,所以创建一个隐藏的临时文件,然后传递给int&&右值参考.代码调用函数2将无法访问此隐藏对象,因此不会发现任何更改.
不同的情况是如果你这样做:
SomeComplexObject o; function2(move(o));
您明确要求o被移动,所以它将根据其移动规范进行修改.但是,移动是一种逻辑上不修改的操作(参见文章).这意味着你是否移动不应该从调用代码中观察到:
SomeComplexObject o; moveit(o); // #1 o = foo;
如果删除移动的行,行为仍然是一样的,因为它被覆盖了.这意味着使用o之后的值已经被移动的代码是不好的,因为它打破了这个隐含的协议在moveit和调用代码之间.因此,标准没有规定从容器移动的具体价值.