繁体   English   中英

具有不可移动/可复制类型的std :: tuple

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM