代码之家  ›  专栏  ›  技术社区  ›  Dennis Zickefoose

关于rvalue参考文献的几点澄清

  •  10
  • Dennis Zickefoose  · 技术社区  · 14 年前

    第一:在哪里 std::move std::forward 定义?我知道它们是做什么的,但我找不到任何标准头都需要包含它们的证据。在GCC44中有时 STD::移动 是可用的,有时不是,所以一个确定的include指令将是有用的。

    在实现移动语义时,源代码可能处于未定义状态。此状态是否必须是对象的有效状态?显然,您需要能够调用对象的析构函数,并能够通过类公开的任何方式分配给它。但其他手术是否有效?我想我要问的是,如果你的类保证了某些不变量,那么当用户说他们不再关心这些不变量时,你是否应该努力实施这些不变量?

    下一步:当您不关心移动语义时,在处理函数参数时,是否有任何限制会导致非常量引用优先于rvalue引用? void function(T&); 结束 void function(T&&); 从调用者的角度来看,能够传递函数临时值有时是有用的,因此似乎只要可行就应该授予该选项。而rvalue引用本身就是左值,因此不能无意中调用move构造函数而不是copy构造函数或类似的东西。我不认为有什么坏处,但我肯定有。

    这就引出了我的最后一个问题。您仍然不能将临时引用绑定到非常量引用。但您可以将它们绑定到非const rvalue引用。然后可以在另一个函数中将该引用作为非常量引用传递。

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

    除了它什么都不做之外,那代码有什么问题吗?我的直觉当然不是,因为改变rvalue引用是它们存在的全部意义。如果传递的值是合法的常量,编译器将捕获它并对您大喊大叫。但从表面上看,这是一个机制的运行,可能是有原因的,所以我只想确认我没有做任何愚蠢的事情。

    3 回复  |  直到 14 年前
        1
  •  10
  •   Lii bob    9 年前

    第一:STD::移动和STD::向前定义?

    20.3 实用组件, <utility> .


    在实现移动语义时,源代码可能处于未定义状态。此状态是否必须是对象的有效状态?

    显然,这个物体应该仍然是毁灭性的。但除此之外,我认为这是一个好主意,仍然可以转让。标准规定满足“可移动构造”和“可移动可分配”的对象:

    [注:RV仍然是有效的对象。其状态未指定。结束注释

    这意味着,我认为,该对象仍然可以参与任何不声明任何先决条件的操作。这包括可复制可构造、可复制可分配、可破坏和其他东西。注意,从核心语言的角度来看,这对您自己的对象不需要任何东西。这些需求只有在您接触到描述这些需求的标准库组件时才会发生。


    下一步:当您不关心移动语义时,在处理函数参数时,是否有任何限制会导致非常量引用优先于rvalue引用?

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

    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选择它确实是正确的)。

    —— 编辑结束 ——


    这就引出了我的最后一个问题。您仍然不能将临时引用绑定到非常量引用。但您可以将它们绑定到非const rvalue引用。

    这在 this article . 第二个调用使用 function2 只接受非标准值。程序的其余部分不会注意到它们是否被修改,因为它们以后将无法访问这些rvalue!以及 5 传递不是类类型,因此将创建一个隐藏的临时项,然后传递给 int&& 右值引用。代码调用 小精灵 无法访问此处的隐藏对象,因此它不会注意到任何更改。

    另一种情况是,如果你这样做:

    SomeComplexObject o;
    function2(move(o));
    

    你已经明确要求 o 移动,因此将根据其移动规范对其进行修改。不管移动是什么 逻辑非修改操作 (见文章)。这意味着您是否移动不应该从调用代码中被观察到:

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

    如果删除移动的行,行为仍将相同,因为它无论如何都会被覆盖。但这意味着使用 o 在它被移走之后 坏的 ,因为它打破了 moveit 还有电话号码。因此,本标准未对从容器中移出的具体值作出规定。

        2
  •  4
  •   avakar    14 年前

    STD::STOP和STD::向前定义?

    std::move std::forward 在中声明 <utility> . 见第20.3节[实用程序]开头的概要。

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

    当然,这取决于如何实现move构造函数和move赋值运算符。但是,如果要在标准容器中使用对象,则必须遵循 MoveConstructible MoveAssignable 概念,它表示对象仍然有效,但保留在 未指定的 国家,也就是说,你绝对可以摧毁它。

        3
  •  2
  •   Ronny Brendel    14 年前

    包括在内 utility


    Here 是我读到的关于rvalues的文章。

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