如何让 class 与 std::mutex 的多个对象?

How to have multiple objects of a class with an std::mutex?

我有以下错误:

filesystem.hpp:11:7: error: use of deleted function ‘std::mutex& std::mutex::operator=(const std::mutex&)’
In file included from /usr/include/c++/6.1.1/mutex:44:0,
                 from includes.hpp:11,
                 from htmlparser.hpp:4,
                 from htmlparser.cpp:1:
/usr/include/c++/6.1.1/bits/std_mutex.h:98:12: note: declared here
     mutex& operator=(const mutex&) = delete;
            ^~~~~~~~

...关于它已经提出了一些问题(例如 and that one)。基于这些,我尝试了以下 class 代码:

class Filesystem {
    private:
        std::string dir = getCurrentPath();
        mutable std::mutex fsMut;
    public:
        Filesystem() {}
        ~Filesystem() {}
        Filesystem(const Filesystem&) : fsMut() { }

        //Few more functions
};

...遗憾的是这不起作用(甚至更改错误)。

现在我的代码与前面提到的问题有何不同:在两个 class 中,我在 private 部分中有声明 Filesystem fs;。然而,在我看来这完全没问题,而对于一个 class 它通过另一个 class 返回此错误(以及 class 和 Filesystem 被隐式删除)。
所以我没有复制或移动 class afaik,但是出了什么问题呢?我怎样才能改变我的代码以使其工作?

编辑:
完整错误:

htmlparser.cpp: In member function ‘strSet Htmlparser::parseLinks(std::__cxx11::string, std::__cxx11::string, std::__cxx11::string)’:
htmlparser.cpp:10:39: error: use of deleted function ‘Robotsparser& Robotsparser::operator=(const Robotsparser&)’
  rsmap[origUrl] = Robotsparser(origUrl);
                                       ^
In file included from htmlparser.hpp:5:0,
                 from htmlparser.cpp:1:
robotsparser.hpp:7:7: note: ‘Robotsparser& Robotsparser::operator=(const Robotsparser&)’ is implicitly deleted because the default definition would be ill-formed:
 class Robotsparser {
       ^~~~~~~~~~~~
robotsparser.hpp:7:7: error: use of deleted function ‘Filesystem& Filesystem::operator=(const Filesystem&)’
In file included from robotsparser.hpp:5:0,
                 from htmlparser.hpp:5,
                 from htmlparser.cpp:1:
filesystem.hpp:11:7: note: ‘Filesystem& Filesystem::operator=(const Filesystem&)’ is implicitly deleted because the default definition would be ill-formed:
 class Filesystem {
       ^~~~~~~~~~
filesystem.hpp:11:7: error: use of deleted function ‘std::mutex& std::mutex::operator=(const std::mutex&)’
In file included from /usr/include/c++/6.1.1/mutex:44:0,
                 from includes.hpp:11,
                 from htmlparser.hpp:4,
                 from htmlparser.cpp:1:
/usr/include/c++/6.1.1/bits/std_mutex.h:98:12: note: declared here
     mutex& operator=(const mutex&) = delete;
            ^~~~~~~~

和其他 classes:

class Robotsparser {
    private:
        std::string url;
        Filesystem fs;
    public:
        Robotsparser(std::string u) : url(u) {}
        ~Robotsparser() {}
};

class A {
    private:
        std::mutex crawlMut;
        Filesystem fs;
    public:
        A(std::string);
};

Class A 是在 Makefile 中较早编译的,这可以解释为什么它在 class Robotsparser.

处给出错误

你的错误意味着有一个赋值操作试图在某处发生...

在你的 Filesystem class 中,编译器没有抱怨复制构造函数:

 Filesystem(const Filesystem&) : fsMut() {} //because there is no copying of fsMut here

但是,由于您没有定义,编译器会为您生成一个复制赋值运算符。而在编译器生成的一个中,它调用每个成员的复制赋值运算符。

我认为您的意图是:您应该定义所有 Copy/Move 赋值运算符(和构造函数),并确保您不尝试 copy/move 任何实例。

 Filesystem(const Filesystem& f2)
 {
      std::lock_guard<std::mutex> lk2(f2.fsMut);
      /*do your stuff but do not copy f2.fsMut*/
 }

 Filesystem(Filesystem&& f2)
 {
      std::lock_guard<std::mutex> lk2(f2.fsMut);
      /*do your stuff but do not move f2.fsMut*/
 }

 Filesystem& operator = (const Filesystem&) 
 {
    std::lock(this->fsMut, f2.fsMut);
    std::lock_guard<std::mutex> lk1(this->fsMut, std::adopt_lock);
    std::lock_guard<std::mutex> lk2(f2.fsMut, std::adopt_lock);

      //do your stuff but do not copy fsMut
       return *this;
 }

 Filesystem& operator = (Filesystem&& f2) 
 {
    std::lock(this->fsMut, f2.fsMut);
    std::lock_guard<std::mutex> lk1(this->fsMut, std::adopt_lock);
    std::lock_guard<std::mutex> lk2(f2.fsMut, std::adopt_lock);

       //do your stuff but do not move fsMut
       return *this;
 }

这里有完整的插图:http://coliru.stacked-crooked.com/a/75d03fd564f8b570 另外,考虑使用 lock_guard's on both mutexes and std::lock 来锁定 copy/move 赋值运算符中的两个互斥量。

虽然我对你的意图仍有保留,但我看到了这样的成员声明:

mutable std::mutex fsMut;

mutable的使用是从const成员函数修改成员;这里通常是为了能够 lock/unlock 来自 const 成员函数的互斥体。

错误的第一位表示什么?

htmlparser.cpp: In member function ‘strSet Htmlparser::parseLinks(std::__cxx11::string, std::__cxx11::string, std::__cxx11::string)’:
htmlparser.cpp:10:39: error: use of deleted function ‘Robotsparser& Robotsparser::operator=(const Robotsparser&)’
  rsmap[origUrl] = Robotsparser(origUrl);

它说在Htmlparser::parseLinks,这一行

  rsmap[origUrl] = Robotsparser(origUrl);

您正在使用不存在的复制赋值运算符。

它不存在的原因是编译器没有为你生成它,因为它不能,因为你的 mutex 成员class 不可复制。

但是,您真正的问题是为什么编译器首先尝试使用它:

So I am not copying or moving the class afaik

但是您可以在编译器引用的行中看到一个赋值。您没有显示 rsmap 是什么,但是查看 std::map shows that it default constructs an element, returns a reference to the new value, and then your code copy-assigns to it. The same is true for std::unordered_map.

的 operator[]

如果您的 class 不可复制或不可分配,您将无法执行此操作 - 您需要就地构建对象。 emplace 方法执行此操作,代码类似于:

rsmap.emplace(origUrl, origUrl);

或者,您可以保留现有代码并编写 copy/move 构造函数和赋值运算符。