假设我有一个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;}
}
如果要使该类可移动,可以添加一个move构造函数。
vector(vector&& other) : size_{other.size_}, data_{other.data_}{other.size_ = 0;}
到目前为止,仅是拼写错误和书呆子注释。
不过,我想使该类可以通过与我的vector
无关的 other 类进行移动。
我知道这基本上需要类似于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]]
并可能导致无关类的内存泄漏。
最糟糕的vector
似乎已经“过早”地从状态中移出了,这远比该对象真正地从(客户端)其他类中移开了。
最后,
也许我所缺少的是某种新型的智能指针,该指针将延迟到以后的时间。
我在这里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
可以达到相同的效果,但我不知道如何实现。
这是更抽象的问题的具体应用:Exact correspondence between r-value references and pointers?
答案 0 :(得分:2)
从根本上讲不可能是一个通用的解决方案。从根本上移出值意味着您需要了解该值类型的实现细节,尤其是它如何管理内存。
因此,您需要以一种或另一种方式(通过友谊,甚至是更公共的界面)发布这些实现细节,这通常是不希望的。无论哪种方式,您都会在类型之间建立紧密的联系,而这是不可避免的。
我也不认为即使有可能,它也不会像您认为的那样有用。
答案 1 :(得分:2)
您已经解决了所有可能性,直接回答您的问题是,不存在任何通用协议。
此答案回顾了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访问权限可用于该元素的关键部分。