代码之家  ›  专栏  ›  技术社区  ›  James Meas

作为复制品传递的STD变量的效果是什么?[关闭]

  •  0
  • James Meas  · 技术社区  · 6 年前

    我刚刚在我当地的图书馆找到了Scott Meyers的有效的现代C++。在我的副本的第4页,Meyers写了不同类型的副本:通过移动构造函数复制和通过复制构造函数复制。给出的示例是以下代码:

    void someFunc(Foo f); 
    Foo bar; 
    someFunc(bar); 
    someFunc(std::move(bar));
    

    我的问题是,函数FUNC如何接受由STD::当参数类型不是rValk引用时移动的rValk引用?

    2 回复  |  直到 6 年前
        1
  •  4
  •   Fureeish    6 年前

    函数需要一个值而不是引用,因此当引用作为参数传递时,将生成某个对象的副本。

    当你通过 左值 引用,参数将成为与所述引用绑定的对象的副本-它正在 复制构造 . 你有一个 对象 ,A 对该对象的引用 和A 基于所述对象创建的复制构造参数 .

    当你通过 价值 参考,似乎不是很多东西都变了。你仍然有 一些物体 一些 对它的引用 正在基于原始对象创建的另一个对象 , 但是 这一次,新创建的对象是通过 移动构造函数 .

    有什么区别?对于基元类型,无。移动和复制 int S char S和S的作用基本相同。当我们用一个以某种方式实际使用move结构的对象对函数进行单元测试时,可以观察到这些差异。我们来看看 std::string 行为:

    void foo(std::string str)
    {
        std::cout << str << " ";
    }
    
    int main()
    {
        std::string first   = "abc";
        std::string& second = first;
        std::string third   = "ghi";
    
        foo(first);             // copy the first string and display it
        foo(second);            // copy the first string and display it
        foo(std::move(third));  // move the third string and display it
    }
    

    在这个例子中,评论说得很少。我们已经确定,在这里我们复制构造,在那里我们移动构造。但这实际上意味着什么?

    移动一个对象并不是那么简单的话题。我鼓励你阅读 this answer 以获得一些见解。丁博士,当你 移动 STD::字符串 ,您不会创建一个全新的副本-您创建了另一个对象,该对象会窃取原始对象的表示并使其保持有效但未指定的状态。

    结论: 前两个电话打给 foo() ,参数为值,参数为值 参考文献,我们以一个简单的 复制 . 第三个电话,我们提供 右值引用 因此,我们强制 移动式建筑 (因为参数类型是一个值,而不是引用,所以它需要一个实际的对象),它将 third 字符串,在内部创建一个全新的字符串 foo s调用堆栈,窃取它的内部指针,离开 第三的 在某种有效的状态下 第三的 开始一个空的 STD::字符串 具有 0 尺寸,但那是 未指定 )并显示移动的字符串。

        2
  •  1
  •   Bo Persson Touseef    6 年前

    在示例代码中:

    void someFunc(Foo f); 
    Foo bar; 
    someFunc(bar); 
    someFunc(std::move(bar));
    

    功能 someFunc 甚至不知道怎么打电话。它总是接受价值 f .


    调用站点上发生的情况取决于 Foo . 可能有一个单独的移动构造函数:

    class Foo
    {
    public:
       Foo();
       Foo(const Foo&);
       Foo(Foo&&);
    };
    

    然后参数将由不同的构造函数处理。


    但也可能是foo只有一个复制构造函数。也许是因为搬家对这门课没什么好处:

    class Foo
    {
    public:
       Foo();
       Foo(const Foo&);
    };
    

    然后,两个调用最终都是相同的。


    甚至可能是(稍微有点反常的)类通过删除move构造函数来禁止移动:

    class Foo
    {
    public:
       Foo();
       Foo(const Foo&);
       Foo(Foo&&) = delete;
    };
    

    然后第二个调用将失败。


    所以-看情况而定!