1

I have the following error:

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;
            ^~~~~~~~

...about which there are already some questions asked (like this one and that one). Based on these I tried the following class code:

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

        //Few more functions
};

...which sadly enough doesn't work (or even change the errors).

Now how does my code differ from the previously mentioned questions: In two classes I have in the private section the declaration Filesystem fs;. Which in my opinion would be totally fine, however, while for one class it passes for the other class this error is returned (along with the errors how that class and Filesystem are deleted implicitly).
So I am not copying or moving the class afaik, but what does go wrong then? And how can I alter my code to make it work?

EDIT:
Full error:

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;
            ^~~~~~~~

And the other 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 is compiled earlier in the Makefile, which might explain why it gives the error at class Robotsparser.

Community
  • 1
  • 1
Simon Klaver
  • 480
  • 5
  • 24
  • This is a problem with copying (and assignment), not with creating multiple instances. When copying happens, what do you want to relationship between copies to be? They can either each get their own unrelated mutex, or you can use `shared_ptr` to have all copies share a single mutex. – Ben Voigt May 18 '16 at 17:58
  • It its really easy to copy an object without meaning to. Common ways are you call a function that takes your object by value, you stuff the object into a container like `vector`, `map`, or `list`, and you copy an object that contains your object. Anyway, unable to reproduce with forced copy of a chopped-down version of `FileSystem`. You may need to edit your question and [provide the great and glorious MCVE.](http://stackoverflow.com/help/mcve) – user4581301 May 18 '16 at 18:00
  • I am not seeing how copying occurs here, that's a particular part of my question. Aside from that, I guess a shared mutex could be nice. How do I implement that? – Simon Klaver May 18 '16 at 18:01
  • D'oh! My brain's off. No `operator=` Possible [Rule of Three violation](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). – user4581301 May 18 '16 at 18:04
  • If you want to know where you are copying, you may be able to find out by hacking your `FileSystem` to delete `operator=`: `Filesystem& operator=(const Filesystem &src) = delete;`. Do the same with your copy constructor, build and read the "called from" trace in the error message (if there is one). – user4581301 May 18 '16 at 18:10
  • 2
    @SimonKlaver The error you posted contains the code `rsmap[origUrl] = Robotsparser(origUrl);` which copies a Robotsparser object. Which you cannot do if that object contains a mutex. – nos May 18 '16 at 18:13
  • @user4581301 Your linked question about the Rule Of Three solved it. I guess we can flag this as a duplicate? EDIT: Hold your horses, maybe my code doesn't do what I want. – Simon Klaver May 18 '16 at 18:14
  • @nos It should create a new object. It doesn't? – Simon Klaver May 18 '16 at 18:15
  • 2
    It creates 2 objects, the `Robotsparser(origUrl)` object on the right hand side, and another object in `rsmap[origUrl]` on the left hand side. Then it "copies", via the assignment operator, the right hand object of the = into the left hand object. – nos May 18 '16 at 18:21

2 Answers2

2

Your error means that there is an assignment operation trying to happen somewhere...

In your Filesystem class, the compiler didn't complain of the copy constructor:

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

But, the compiler generates a copy assignment operator for you since you didn't define one. And in the compiler generated one, it calls the copy assignment operator of each member.

For what I think your intent are: You should define all your Copy/Move Assignment operators (and Constructors), and make sure you do not try to copy/move the mutex owned by any of the instances.

 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;
 }

Full illustration here: http://coliru.stacked-crooked.com/a/75d03fd564f8b570 Also, consider using, lock_guard's on both mutexes and std::lock to lock both mutexes in the copy/move assignment operators.

Though I still have my reservations on your intent, I saw a member declaration like:

mutable std::mutex fsMut;

The use of mutable is to modify members from a const member function; here its typically for being able to lock/unlock the mutex from a const member function.

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
0

What does the first bit of the error say?

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);

It says that in Htmlparser::parseLinks, on this line

  rsmap[origUrl] = Robotsparser(origUrl);

you're using a copy assignment operator that doesn't exist.

The reason it doesn't exist is that the compiler didn't generate it for you, because it can't, because the mutex member of your class isn't copyable.

However, your real question is why the compiler is trying to use this in the first place:

So I am not copying or moving the class afaik

but you can see, on the line the compiler quoted, an assignment. You haven't shown what rsmap is, but looking at operator[] for 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.

If your class isn't copyable or assignable, you can't do this - you need to construct the object in place. The emplace method does this, giving code something like:

rsmap.emplace(origUrl, origUrl);

Alternatively, you can keep your existing code and write the copy/move constructors and assignment operators.

Useless
  • 64,155
  • 6
  • 88
  • 132