如何使一个类可被其他类移动



假设我有一个RAII类,比如vector:

class vector{
double* data_;
int size_;
public:
explicit vector(int size = 0) : data_{new double[size]}, size_{size}{}
vector(vector const& other) : data_{new double[other.size_]}, size_{other.size_}{}
int size() const{return size_;}
double const* data() const{return data_;}
double* data(){return data_;} // optional but common
~vector(){if(size_) delete[] double;}
}

如果我想使类可移动,我可以添加一个移动构造函数。

vector(vector&& other) : size_{other.size_}, data_{other.data_}{other.size_ = 0;}

到目前为止,模错别字和迂腐的评论,仅此而已。

但是,我希望通过与我的vector无关的其他类来移动该类。我知道这基本上需要类似于move构造函数的代码,但在一个独立的类中。

class SuperVector{
double* data_begin_;
double* data_end_; // I don't use size to show having two independent implementations
std::string super = "super";
SuperVector(vector&& v) ... {...} // what here? what needs to change in `vector`?
};

我几乎可以肯定,无论SuperVector代码是什么,vector都需要以某种方式进行更改以允许这样做。

问题是,是否有一种协议通常用于允许不相关类的这些可移动性。或者这是一件还没有考虑到的事情。(我想偶尔人们会想从std::vector转移到一个不相关的类中(


前期工作:

我可以想象的解决方案是:

  1. 使SuperVector成为vector朋友,然后实现移动";变成";简称

    SuperVector(vector&& v) : data_begin_{v.data()}, data_end_{v.data() + v.size()}{v.size_ = 0;}
    

    这样做的问题是,友谊会增加很多耦合,而且这不是一个通用的解决方案。

  2. 提供对向量内部表示的更多访问权限(特别是使其可赋值(。

    int& vector::size(){return size_;}
    
    SuperVector(vector&& v) : data_begin_{v.data()}, data_end_{v.data() + v.size()}{v.size() = 0;}
    

    这真的很糟糕,因为任何人都可以更改size、中断不变量等。

    现在是更复杂和非琐碎的选择。

  3. 类似2(,但添加了特殊功能:

    class vector{...
    [[nodiscard]] // false sense of security
    double* moved_data()&&{ // for lack of a better name (simply data()&&?)
    size_ = 0;
    return data_;
    }
    ...}
    

    并将其用作

    SuperVector(vector&& v){ // or some variation of this
    data_end_ = v.data() + v.size()}; 
    data_begin_ = std::move(v).moved_data();
    }
    

    这样做的缺点是vector需要修改,这是意料之中的。但是,任何人都可以调用moved_data并使vector无效,这似乎也是危险的。此外,这取决于[[nodiscard]],并且可能会从不相关的类中产生内存泄漏。最糟糕的是,CCD_;过早地";,在对象真正从(客户端(其他类中移动之前很久。

    最后,

  4. 也许我缺少的是某种新的智能指针,它会将移动推迟到稍后的时间。我把它叫做move_ptr

    template<class CustomMover>
    class move_ptr{ // or transfer_ptr
    double* ptr_;
    CustomMover mover_;
    public:
    move_ptr(double* ptr, CustomMover mover) : ptr_{ptr}, mover_{std::move(mover)}{}
    // probably movable too.
    [[nodiscard]] operator double*(){ // protect against twice move
    if(ptr_) mover_();
    auto ret = ptr_; ptr_ = nullptr; return ret;
    }
    //   ~move_ptr(){} // if nobody took care, that is ok, nothing happens.
    }
    
    class vector{...
    auto moved_data()&&{ // for lack of a better name (or simply data()&&?)
    auto custom_mover = [&size_]{size_ = 0;};
    return move_ptr<decltype(custom_mover)>{data_, custom_mover_};
    }
    ...}
    

    (其他可能的名称是move()&mdata()&mdata()&&data()&&。(

    请注意,移动的副作用由智能指针携带。

    并将其用作

    SuperVector(vector&& v){ // or some variation of this
    data_end_ = v.data() + v.size()}; 
    data_begin_ = std::move(v).moved_data(); // this will set size to zero
    }
    

    我确信有一些边缘情况我没有考虑这个简单的代码,但我希望主要的想法是明确的。

    欢迎进行修复。

    这个智能指针有意义吗;移动管理器指针";已经在某个地方实施了

    我看到这与std::unique_ptr类似,只是移动是由从智能指针复制的指针实现的,而且自定义操作仅在分配(到常规指针(时发生。

    也许std::unique_ptr也能达到同样的效果,但我不知道怎么做。

这是对这个更抽象的问题的具体应用:r值引用和指针之间的精确对应?

您已经讨论了所有的可能性,对您的问题的直接答案是不存在通用协议。

这个答案回顾了std::lib中允许将所有权转移到不相关类型的几个地方。

unique_ptr

auto p = up.release();

unique_ptrup已经放弃了对其资源的所有权,现在由客户端负责

unique_lock

auto m = ul.release();

unique_lockul已经放弃了对其互斥锁的锁定状态的所有权,现在由客户端负责

关联容器和无序容器

extract成员函数查找单个元素(通过键或迭代器(,并释放名为container::node_type的类似智能指针的对象中单个节点的所有权。这个";智能指针";有一个分配器的副本,这样它就知道如何自毁。它还为客户端提供对所包含元素的非常量访问。当元素的键部分为容器所有时,只有const访问权限可用于该元素。

根本不可能有一个通用的解决方案。从根本上说,移出一个值意味着您需要了解该值类型的实现细节,特别是它如何管理内存。

因此,您需要以某种方式(通过友谊或更公开的接口(发布这些实现细节,这通常是不可取的。无论哪种方式,类型之间都会出现紧密耦合,这是无法避免的。

我也不认为,即使可能,这会像你想象的那样普遍有用。

相关内容

  • 没有找到相关文章

最新更新