如何在c++ 11中使用右值复制或别名对象

How do I copy or alias an object in C++11 using rvalues?

本文关键字:复制 对象 别名 c++      更新时间:2023-10-16

我想知道我对这个问题的理解是否正确,如果正确,如何解决。

我正在编写一个模板类来存储一些类型对象并对它们执行操作。问题是,我的技能仍然较差,所以即使读了11页关于价值的书,我也不能完全理解。在我的模板类中,如果我理解正确的话,一个重载的右值复制函数会使主函数中的代码在执行"cout <<"Ex.z;'被执行,因为而不是'test = move(ex);'调用左值复制函数,它会调用右值,这将导致'ex'引用什么测试引用和'test'引用什么'ex'引用?

如果这是正确的,我如何正确地实现右值对象复制功能?我之所以能够做到这一点,主要是为了创建一个正确接受右值的构造函数,而不是像我的左值构造函数那样工作,而迂回的方法是确保我理解它。

template<class T>
class Vec3
{
public:
    Vec3(){}
    Vec3(const Vec3 &vec):x(vec.x),y(vec.y),z(vec.z){}
    void operator = (const Vec3 &other)
    {x=other.x; y=other.y; z=other.z;}
//    void operator = (Vec3 &&other)
//    {
//this would just call the other overloaded copy function
//        *this = move(other);
//    }
    T x, y, z;
};
main(){
  Vec3<int> ex(0,0,0);
  Vec3<int> test = move(ex);
  test.z++;
  cout << test.z;//will be 1
  cout << ex.z;//will be 0
}

应该是这样的:

#include <iostream>
template<class T>
class Vec3
{
public:
    Vec3() {}
    Vec3(T x_, T y_, T z_): x(x_), y(y_), z(z_)
    {}
    Vec3(const Vec3 &vec):x(vec.x),y(vec.y),z(vec.z)
    {}
    Vec3(Vec3 &&vec) noexcept : x(std::move(vec.x)),y(std::move(vec.y)),z(std::move(vec.z))
    {}
    Vec3& operator=(const Vec3 &other)
    {
        x=other.x; y=other.y; z=other.z;
        return *this;
    }
    Vec3& operator=(Vec3 &&other) noexcept
    {
        x = std::move(other.x);
        y = std::move(other.y);
        z = std::move(other.z);
        return *this;
    }
    T x, y, z;
};
int main(){
  Vec3<int> ex(0,0,0);
  Vec3<int> ex1(1,1,1);
  ex1 = std::move(ex); // <-- this will call move assignment operator
  Vec3<int> test = std::move(ex); // <-- this will call move constructor
  test.z++;
  std::cout << test.z;//will be 1
  std::cout << ex.z;//will be 0
  return 0;
}

我个人更喜欢使用swap习语:

#include <iostream>
template<class T>
class Vec3
{
public:
    Vec3() {}
    Vec3(T x_, T y_, T z_): x(x_), y(y_), z(z_)
    {}
    Vec3(const Vec3 &vec):x(vec.x),y(vec.y),z(vec.z)
    {}
    Vec3(Vec3 &&other) noexcept
    { swap(other); }
    Vec3& operator=(const Vec3 &other)
    {
        x=other.x; y=other.y; z=other.z;
        return *this;
    }
    Vec3& operator=(Vec3 &&other) noexcept
    {
        Vec3{std::move(other)}.swap(*this);
        return *this;
    }
    void swap(Vec3 &other) noexcept
    {
        std::swap(x, other.x);
        std::swap(y, other.y);
        std::swap(z, other.z);
    }
    T x, y, z;
};
main(){
  Vec3<int> ex(0,0,0);
  Vec3<int> ex1(1,1,1);
  ex1 = std::move(ex); // <-- this will call move assignment operator
  Vec3<int> test = std::move(ex); // <-- this will call move constructor
  test.z++;
  std::cout << test.z;//will be 1
  std::cout << ex.z;//will be 0
}

添加noexcept到您的移动构造函数将受益,当在STL容器中使用std::vector等类时。容器将使用移动而不是复制

实际上,你误解了与右值引用无关的

简单地说,Vec3<int> test = move(ex)将用一个参数调用Vec3<int>的move构造函数,一个对ex的右值引用。

要理解这一点,你必须理解std::move()。这个模板函数所做的是接受左值引用或右值(纯右值,也就是我们所谓的"右值",因为它的起源是一个长,)引用,并将其转换为xvalue(表达值,即可以移动数据的值)。这样的值将绑定到其类型的右值引用。

在所有这些混乱之后,简单的解决方案是实现一个move构造函数:
Vec3(Vec3 &&vec) : x(std::move(vec.x)), y(std::move(vec.y)), z(std::move(vec.z)) {}

您也可以将相同的原则应用于赋值操作符:

void operator=(Vec3 &&vec) {
    this->x = std::move(vec.x);
    this->y = std::move(vec.y);
    this->z = std::move(vec.z);
}

重要的:不要简单地复制粘贴最后的代码片段!c++ 11引入了大量复杂的术语,(通常)一开始很难掌握。如果你不理解这篇文章,阅读更多的解释/例子直到你理解,否则你的大脑会调用神经病学ABI规范定义的8位智人(非图灵完全)架构,由开放自然选择组织在公元前75000年发布。