5

Below is minimal code to reproduce the error.

#include <iostream>
#include <mutex>
#include <vector>

class A {
    std::mutex mutex;
    public:
    A(){};
};
int main() 
{
    std::vector<std::pair<std::string,A>> aa;
    A a;
    //aa.push_back(std::make_pair(std::string("aa"),A()));
    //aa.push_back(std::make_pair(std::string("aa"),a));
    aa.push_back(std::make_pair(std::string("aa"),std::move(a)));    
}

Below is error.

Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27026.1 for x64 Copyright (C) Microsoft Corporation. All rights reserved.

>   C:\Program Files (x86)\Microsoft Visual
> Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\xlocale(319):
> warning C4530: C++ exception handler used, but unwind semantics are
> not enabled. Specify /EHsc    C:\Program Files (x86)\Microsoft Visual
> Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\utility(405):
> error C2440: '<function-style-cast>': cannot convert from 'initializer
> list' to '_Mypair'    C:\Program Files (x86)\Microsoft Visual
> Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\utility(405):
> note: No constructor could take the source type, or constructor
> overload resolution was ambiguous
>   ..\examples\json_object\json.cpp(16): note: see reference to function
> template instantiation 'std::pair<std::string,A>
> std::make_pair<std::string,A>(_Ty1 &&,_Ty2 &&)' being compiled            with
>           [
>               _Ty1=std::string,
>               _Ty2=A          ]

Similar error for gcc compiler. When I remove std::mutex from class OR don't push the object on std::vector, then it compiles fine.

P.W
  • 26,289
  • 6
  • 39
  • 76
Manthan Tilva
  • 3,135
  • 2
  • 17
  • 41

3 Answers3

11

As per the documentation on std::mutex.

std::mutex is neither copyable nor movable.

Since class A contains a std::mutex variable mutex, it is not movable either.

P.W
  • 26,289
  • 6
  • 39
  • 76
5

As P.W. pointed out, std::mutex is neither copyable nor movable, and for good reason. The whole point of having a mutex is to protect against simultaneous multithreaded access to some data. The move operation itself needs to be protected, and the mutex should be used by the move operation.

The following example gives the class some movable data and shows how the mutex should be used in a move operation (copy operations would be similar):

#include <iostream>
#include <mutex>
#include <vector>
#include <memory>

class A {
public:
  A() {};

  // Move constructor
  A(A&& other) {
    std::lock_guard<std::mutex> guard(other.m_mutex);
    m_data = std::move(other.m_data);
  }

  // Move operator
  A& operator=(A&& other) {
    if (this == &other) return *this;

    // Lock this and other in a consistent order to prevent deadlock
    std::mutex* first;
    std::mutex* second;
    if (this < &other) {
      first = &this->m_mutex;
      second = &other.m_mutex;
    } else {
      first = &other.m_mutex;
      second = &this->m_mutex;
    }
    std::lock_guard<std::mutex> guard1(*first);
    std::lock_guard<std::mutex> guard2(*second);

    // Now both this and other are safe to access.  Do the actual data move.
    m_data = std::move(other.m_data);
    return *this;
  }

private:
  std::mutex m_mutex;
  std::unique_ptr<int> m_data;
};

int main() {
  std::vector<std::pair<std::string,A>> aa;
  A a1;
  A a2;
  a1 = std::move(a2);
  aa.emplace_back("aa", std::move(a1));
}
Jay West
  • 1,137
  • 8
  • 5
  • Nice answer. This answer by Howard Hinnant shows a simpler way to avoid deadlock using `std::unique_lock`, `std::defer_lock` and `std::lock`: https://stackoverflow.com/a/29988626/1047213 – Hari Jun 21 '23 at 12:27
2

As pointed by P.W and hint provided by freakish I come up with below solution.

#include <iostream>
#include <mutex>
#include <vector>
#include <memory>

class A {
    std::mutex mutex;
    public:
    A(){};
};
int main() 
{
    std::vector<std::pair<std::string,std::shared_ptr<A>>> aa;
    A a;
    //aa.push_back(std::make_pair(std::string("aa"),A()));
    //aa.push_back(std::make_pair(std::string("aa"),a));
    aa.push_back(std::make_pair(std::string("aa"),std::make_shared<A>()));   
}

I modified my container to store smart pointer of object instead of object itself.

Manthan Tilva
  • 3,135
  • 2
  • 17
  • 41
  • 5
    FWIW, you should really use `std::unique_ptr` unless you *really* need shared ownership. 90% of the time, you don't. – NathanOliver Mar 05 '19 at 13:35