[英]std::tuple with non moveable/copyable type
假设我有一个结构MyStruct
,它包含一个std::mutex
,只包含一个引用或pod成员。 我想创建一个包含此类型的std::tuple
。 但由于MyStruct
不是复制或可移动的,所以它不起作用。 如果我按以下方式添加移动构造函数,它会工作:
#include <tuple>
#include <mutex>
#include <vector>
struct MyStruct {
std::vector<double>& vec_;
int number_;
std::mutex mutex_;
MyStruct(std::vector<double>& vec, const int number)
: vec_(vec), number_(number) {}
MyStruct(MyStruct&& o) : vec_(o.vec_), number_(o.number_)
{
std::lock_guard guard(o.mutex_);
}
};
int main()
{
using T = std::tuple<MyStruct,int>;
std::vector<double> v{1., 2., 3.};
T t{MyStruct{v, 1}, 2};
}
这样安全吗? 最后,我只想构建它里面std::tuple
的任何多线程之前是怎么回事,但我怎么能保证的元素std::tuple
不会移动,一旦std::tuple
构造? 或者有没有办法构建一个std::tuple
?
您的代码在特定情况下可能是安全的,但如果您希望它一般是安全的,那么您需要修复移动构造函数。 移动构造函数必须正确地同步对移动对象的状态的访问,并且必须防止该对象在新对象被盗后修改状态:
class MyStruct {
std::vector<double>* vec_; // use a pointer here, not a reference
int number_;
std::mutex mutex_;
public:
MyStruct(std::vector<double>& vec, const int number)
: vec_(&vec), number_(number) {}
MyStruct(MyStruct&& o)
{
std::lock_guard guard(o.mutex_);
vec_ = std::exchange(o.vec_, nullptr);
number_ = std::exchange(o.number_, 0); // doesn't necessarily have to be 0 here
}
};
您还必须更改任何其他方法以考虑移出状态。 另请注意,我已将此更改为类并使该状态为私有。 拥有公共互斥体和公共数据成员会带来麻烦。 为安全起见,您必须确保所有对可变状态的访问都已正确同步。
没有Move Constructor
如果你想避免移动构造函数,那么最明显的解决方案是避免使用tuple
。 tuple
旨在与库中的通用代码一起使用。 存在其他上下文中的有效用途但很少见。 考虑使用结构来存储值:
struct Fixture
{
MyStruct a;
int b;
};
这样做的好处是命名您的成员(更易读)并允许自定义构造函数。
使用std :: tuple
如果你绝对必须使用元组,那么问题仍然是可以解决的。 在构造元组之后,您只需要一种构造MyStruct
对象的方法。 有几种方法可以做到这一点。 这是两个:
使用std::optional
:
int main()
{
using T = std::tuple<std::optional<MyStruct>,int>;
std::vector<double> v{1., 2., 3.};
T t{std::nullopt, 2};
std::get<0>(t).emplace(v, 1);
}
定义默认构造函数并使用两阶段初始化:
// Add a constructor like the following, once again using a pointer instead of reference
MyStruct() : vec_(nullptr), number_(0) {}
// Add an init method
void init(std::vector<double>& vec, int number)
{
vec_ = &vec;
number_ = number;
}
// Later
int main()
{
using T = std::tuple<MyStruct,int>;
std::vector<double> v{1., 2., 3.};
T t{MyStruct{}, 2};
std::get<0>(t).init(v, 1);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.