2

I'm having an issue with declaring a mutex in a class, whenever I attempt instantiate the class the Error C2280 appears. My project is too create a simulation of the Dining Philosopher's Problem.

Declaration:

class Fork
{

public:

struct _position
{
    float x;
    float y;
};

Fork();
Fork(int num_phil_, int new_index_);
_position ReturnWorkPosition(){ _position return_position_; /*position_mutex_.lock();*/ return_position_ = work_position_; 
                                    /*position_mutex_.unlock();*/ return return_position_; };
float ReturnRotation(){ return rotation_; };

void CalculateWorkPosition(int master_index_);
void ResetWorkPosition(){ /*position_mutex_.lock();*/ work_position_ = orig_position_; /*position_mutex_.unlock();*/ };
void TakeOwnership(int master_index_);
void RelinquishOwnership();

private:

int index_;
int num_philosophers_;
float rotation_;

_position orig_position_;
_position work_position_;

//std::mutex master_mutex_;
//std::mutex position_mutex_;
};

Instantiation:

for (int i = 0; i < num_philosophers_; i++)
{
    Fork temp_fork_(num_philosophers_, i);
    fork_.push_back(temp_fork_);
}

Edit:: New Declaration:

class Fork
{

public:

Fork();
Fork(int num_phil_, int new_index_);

Fork(Fork const&) = delete;
Fork& operator=(Fork const&) = delete;

struct _position
{
    float x;
    float y;
};

_position ReturnWorkPosition(){ _position return_position_; /*position_mutex_.lock();*/ return_position_ = work_position_; 
                                    /*position_mutex_.unlock();*/ return return_position_; };
float ReturnRotation(){ return rotation_; };

void CalculateWorkPosition(int master_index_);
void ResetWorkPosition(){ /*position_mutex_.lock();*/ work_position_ = orig_position_; /*position_mutex_.unlock();*/ };
void TakeOwnership(int master_index_);
void RelinquishOwnership();

private:

int index_;
int num_philosophers_;
float rotation_;

_position orig_position_;
_position work_position_;

//std::mutex master_mutex_;
//std::mutex position_mutex_;
};

Error messages:

Error   18  error C2280: 'Fork &Fork::operator =(const Fork &)' : attempting to reference a deleted function    c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility 2045    1   Lab3e

Error   13  error C2280: 'Fork::Fork(const Fork &)' : attempting to reference a deleted function    c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0 600 1   Lab3e

New Instantiation:

for (int i = 0; i < num_philosophers_; i++)
{
    Fork temp_fork_(num_philosophers_, i);
    fork_.emplace_back(temp_fork_);
}
Alex L
  • 115
  • 11
  • Related to your question: http://stackoverflow.com/questions/16465633/how-can-i-use-something-like-stdvectorstdmutex – MikeMB Jul 17 '15 at 00:05

2 Answers2

2

You need to delete your copy constructor and copy-assignment operator. You cannot copy a std::mutex and therefore you cannot copy an instance of your class, you can only construct or move one.

Fork(Fork const&) = delete;
Fork& operator=(Fork const&) = delete;

The error is telling you that you are trying to reference a deleted function, which is the copy constructor, because push_back is trying to copy your temp_fork_. Instead, I would use emplace_back to create a Fork in-place.

for (int i = 0; i < num_philosophers_; i++)
{
    fork_.emplace_back(num_philosophers_, i);
}

Or

for (int i = 0; i < num_philosophers_; i++)
{
    Fork temp_fork_(num_philosophers_, i);
    fork_.push_back(std::move(temp_fork_));
}

Side note
Instead of manually locking and unlocking your std::mutex, you can do so using a std::lock_guard, which is a class set up to handle locking/unlocking a mutex via RAII

void ResetWorkPosition()
{
    std::lock_guard<std::mutex> lock(position_mutex_);  // position_mutex_ is now locked
    work_position_ = orig_position_;
};  // lock fell out of scope, position_mutex_ is now unlocked

_position ReturnWorkPosition()
{
    _position return_position_;
    std::lock_guard<std::mutex> lock(position_mutex_);  // position_mutex_ is now locked
    return_position_ = work_position_;
    return return_position_;
};
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • I'm not sure I follow, `return` what, the vector `fork_`? – Cory Kramer Jul 16 '15 at 22:29
  • Oh sorry, I meant the lock guard. Also where should I place the code to delete the copy constructor? Thanks. – Alex L Jul 16 '15 at 22:33
  • @AlexL You don't need to `return` the `lock_guard`, it is a function local variable and you want it to fall out of scope, when it does it will call `unlock` on the mutex for you. You should put the deleted copy constructors in your class body, probably right after you declare your constructors. – Cory Kramer Jul 16 '15 at 22:34
  • Sorry really not explaining myself properly, when I'm calling a `return` function, will the lock guard work? As shown in the original code, I have to create a temporary variable to return since I wouldn't be able to unlock the mutex. Also I'm getting the Error C2280 for the copy constructors now. – Alex L Jul 16 '15 at 22:54
  • Can you please edit your post to include the full error message you are seeing? – Cory Kramer Jul 16 '15 at 22:59
  • Where is it indicating that error is coming from? It is indeed trying to invoke the copy constructor, but where? Did it show you the line that is causing the problem? – Cory Kramer Jul 16 '15 at 23:11
  • It claims to be coming from files called xutility and xmemory0 respectively, the error is thrown before the code changes you recommended (seen through breakpoint). – Alex L Jul 16 '15 at 23:14
  • I cannot see anywhere that would be causing a copy of a `Fork`, as long as you make the change to `emplace_back` that I mentioned at the top of my post. – Cory Kramer Jul 16 '15 at 23:18
  • `mutex` is not `move-constructible` nor `move-assignable`. You have to define the move-constructor and move-assignment of `Fork` to do something meaningful. – sbabbi Jul 16 '15 at 23:27
  • That's the problem! You didn't follow my advice :) That's not how you use `emplace_back`. You either need to **construct in place** or **move**, you cannot **copy**. See the two versions of the `for` loop in my post. – Cory Kramer Jul 16 '15 at 23:27
  • Ah my mistake, I've corrected the instantiation. However I am still getting the error, even if I comment out the instantiation. – Alex L Jul 16 '15 at 23:34
  • 1
    Why would you explicitly have to delete those functions, when they are already implicitly deleted? Also std::vector needs a type that is - I believe - either copyable or noexcept-movable, so as sbabbi mentioned, you have to at least define one of those functions. – MikeMB Jul 16 '15 at 23:40
  • What is "a `return` function"? – Lightness Races in Orbit Jul 16 '15 at 23:42
  • @MikeMB: In what sense are they "implicitly deleted"? – Lightness Races in Orbit Jul 16 '15 at 23:43
  • @LightnessRacesinOrbit: Maybe I've used the wrong term, what I meant was that they are automatically defined as deleted, because some of `Fork`'s members (the mutexes) cannot be copy-constructed or assigned – MikeMB Jul 16 '15 at 23:50
  • @LightnessRacesinOrbit: I assume he means a function, that returns a value as e.g. `ReturnWorkPosition()` does. – MikeMB Jul 16 '15 at 23:51
  • @AlexL: Yes, that is exactly the advantage of lock_guard: it will unlock the mutex upon the (automatic) destruction of the lock_guard object, regardless of how you leave the function. – MikeMB Jul 16 '15 at 23:53
  • @MikeMB: That doesn't define them as `deleted`. (Whether this is intuitive or not I leave up to you :P) – Lightness Races in Orbit Jul 16 '15 at 23:57
  • This seems to have gone off topic, the original problem still hasn't be solved. The original error is still thrown even if I don't instantiate the `Fork` object. – Alex L Jul 17 '15 at 00:09
  • @AlexL: sabbi gave you the answer. Also, have you read the question and the answers I linked to? – MikeMB Jul 17 '15 at 00:11
  • 1
    @LightnessRacesinOrbit: So what is the correct terminology here? Reading the standard is not my strong suit, but §12.8 23 in the draft for c++14 says: `A **defaulted** copy/move assignment operator for class X is **defined as deleted** if X has: [...] (23.4) a potentially constructed subobject of class type M (or array thereof) that cannot be copied/moved [...]` and e.g. point 20 before that states `If the definition of a class X does not explicitly declare a move assignment operator, one will be **implicitly declared as defaulted** if [...]`. Thats what I mixed up to **implicitly deleted** – MikeMB Jul 17 '15 at 00:22
  • @MikeMB: Huh, fair enough! TIL. Then yes, the first part of this answer is not accurate, in that manually deleting these functions isn't required. – Lightness Races in Orbit Jul 17 '15 at 00:27
  • @MikeMB I can't seem to find the link you are talking about, I'm not sure if I'm just being stupid now? – Alex L Jul 17 '15 at 02:07
  • @LightnessRacesinOrbit: And the second part is also wrong, because Fork is also not move_constructable, which is necessary for those solutions. – MikeMB Jul 17 '15 at 07:44
  • @AlexL: I meant the link in the my comment under your question. I think I put the most relevant parts in my answer. – MikeMB Jul 17 '15 at 07:50
  • @MikeMB: IIRC it's not "necessary" until you actually cause it to be so, physically. Something that always surprised me somewhat. – Lightness Races in Orbit Jul 17 '15 at 09:41
  • @LightnessRacesinOrbit: Correct. According to cppreference, this changed with c++11 in that there is no longer a global requirement (*"To be used in `vector`, `T` is requried to be ...."*) but each actually used function puts its own requirements on the type. The problem is that even `emplace_back` as well as `push_back move` require `T` to be MoveInsertable, which usually requires a move constructor. That is the reason, why the offered solution doesn't work for the OP. – MikeMB Jul 17 '15 at 10:48
0

As @sbabbi pointed out in the comments to @CoryKramer's answer, essentially, the problem is that your class needs to have a copy or move constructor and because some of its members (the mutexes) don't have one of their own, the compiler implicitly deletes (my term) those special functions for Fork. So you have to implement at least one of them on your own for Fork or use pointers (see below).

Explanation

In order to be storable in a container, your class T has to fulfill certain requirements. For a std::vector and until c++11, T had to be CopyAssignable and CopyConstructible. Since c++11 however, the exact requirements depend on the functions you are actually trying to call (T always has to be erasable) .

Essentially, this means, your type has to have a copy constructor when you want to use push_back and even if you are using emplace_back, you still need at least a move constructor. The rational is that if a push/emplace_back causes a re-allocation, the elements have to be moved from the old to the new memory region.

Now copying or moving a mutex doesn't make much sense, which is why it doesn't have any of those member functions (they are declared as deleted by the standard). As a result, your compiler also doesn't implicitly define those copy/move constructors for your Fork class, but rather implicitly deletes them and then gives you the error that you are trying to use a deleted function.

Possible solutions

You can define your own copy constructor, which would look something like this:

Fork::Fork(const Fork& other) {
    /* potentially locking a mutex of other, 
       which would have to be declared mutable */
    index_            =other.index_;
    num_philosophers_ =other.num_philosophers_;
    rotation_         =other.rotation_;
    orig_position_    =other.orig_position_;
    work_position_    =other.work_position_;
}

The problem is that it might be hard to define the correct semantics of a copy constructor in the context of your (multi-threaded) program. If you only need it for inserting Forks into a vector, that isn't modified during the further execution of your program, you should be fine with just ignoring the mutexes though.

However, it is probably better to store a pointer to a Fork instead:

for (int i = 0; i < num_philosophers_; i++) {      
    fork_.push_back(std::unique_ptr<Fork>(new Fork(num_philosophers_, i)));
    //or if your compiler is c++14 compatible:
    fork_.push_back(std::make_unique<Fork>(num_philosophers_, i));      
}

Thirdly, you can use a different container like std::list instead of vector (you'd still have to use emplace_back instead of push_back though).

MikeMB
  • 20,029
  • 9
  • 57
  • 102
  • Hi thank you for your answer. I've added in the Copy Constructor you posted as a solution, however the error still persists. Interestingly despite you saying that the error should only occur when I try `push_back`, the error still occurs even if I don't attempt to use the vector at all and only declare it `std::vector fork_;`. Any help would be greatly appreciated. – Alex L Jul 17 '15 at 12:56
  • @AlexL: What compiler are you using? Please provide a [MCVE](http://stackoverflow.com/help/mcve) – MikeMB Jul 17 '15 at 19:28
  • Uhh... I'm using Visual Studio Professional 2013? I've tried to create a new application using the `Fork` class and a `vector` to store them but it compiles fine which is rather puzzling. The actual project is an OpenGL project if that could be an issue. – Alex L Jul 17 '15 at 22:36
  • I've added a link to my full project, this one compiles fine and shows the graphical interface. If you go into `Fork.h` and uncomment either of the `mutexes` then you will get the error. [link](https://www.dropbox.com/s/b8j7td9hube2d4h/Coursework.zip?dl=0) – Alex L Jul 17 '15 at 22:48
  • @AlexL: Sorry, I hope you understand that I'll not analyze your whole application for you. I’d recommend just copying your project and then removing unrelated parts, until the error vanishes. Aside from that: Your compiler is complaining about a missing copy assignment operator too, so you probably should also implement that one, too. – MikeMB Jul 18 '15 at 22:02
  • Completely understandable, you've already given me fantastic help which I'm really grateful for. I've discovered however that the `Fork` class itself is not the issue but rather the `Philosopher` class, which contains a `Fork` vector. I've added a copy constructor to the `Philosopher` class however it still has the same issues. Altering the class to not use the `Fork` class allows the project to compile. EDIT:: The error is fired off when in the constructor for `Philosopher` I attempt to make the `Fork` vector in the class equal to the `Fork` vector being passed in. – Alex L Jul 18 '15 at 23:00