假设我有一个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
转移到一个不相关的类中(
前期工作:
我可以想象的解决方案是:
使
SuperVector
成为vector
的朋友,然后实现移动";变成";简称SuperVector(vector&& v) : data_begin_{v.data()}, data_end_{v.data() + v.size()}{v.size_ = 0;}
这样做的问题是,友谊会增加很多耦合,而且这不是一个通用的解决方案。
提供对向量内部表示的更多访问权限(特别是使其可赋值(。
int& vector::size(){return size_;}
SuperVector(vector&& v) : data_begin_{v.data()}, data_end_{v.data() + v.size()}{v.size() = 0;}
这真的很糟糕,因为任何人都可以更改
size
、中断不变量等。现在是更复杂和非琐碎的选择。
类似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_;过早地";,在对象真正从(客户端(其他类中移动之前很久。最后,
也许我缺少的是某种新的智能指针,它会将移动推迟到稍后的时间。我把它叫做
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_ptr
up
已经放弃了对其资源的所有权,现在由客户端负责
unique_lock
auto m = ul.release();
unique_lock
ul
已经放弃了对其互斥锁的锁定状态的所有权,现在由客户端负责
关联容器和无序容器
extract
成员函数查找单个元素(通过键或迭代器(,并释放名为container::node_type
的类似智能指针的对象中单个节点的所有权。这个";智能指针";有一个分配器的副本,这样它就知道如何自毁。它还为客户端提供对所包含元素的非常量访问。当元素的键部分为容器所有时,只有const访问权限可用于该元素。
根本不可能有一个通用的解决方案。从根本上说,移出一个值意味着您需要了解该值类型的实现细节,特别是它如何管理内存。
因此,您需要以某种方式(通过友谊或更公开的接口(发布这些实现细节,这通常是不可取的。无论哪种方式,类型之间都会出现紧密耦合,这是无法避免的。
我也不认为,即使可能,这会像你想象的那样普遍有用。