I've just been looking at solutions to another question (this one). It seems that it should be possible to create a custom deleter for a unique_ptr
instance that can unlock a mutex.
For example,
#include <mutex>
#include <memory>
struct LockDeleter
{
std::unique_lock<std::mutex> lock_;
LockDeleter(std::mutex& m) : lock_(m) {}
void operator()(void*) { lock_.unlock(); }
};
int main()
{
std::mutex moo;
{
std::unique_ptr<int, LockDeleter> ptr(new int(42), LockDeleter(moo));
}
}
Compiling this under VS2013 express, I get
Error 1 error C2280: 'std::unique_lock::unique_lock(const std::unique_lock &)' : attempting to reference a deleted function
and
This diagnostic occurred in the compiler generated function 'LockDeleter::LockDeleter(const LockDeleter &)'
Now, I can't seem to force the lock deleter instance to be moved, rather than copied using std::move
... even adding an explicit move constructor to LockDeleter
doesn't help, and the deleted copy constructor still gets called.
So, am I doing something silly, or must unique_ptr
deleters always be copy constructable?
unique_ptr
is required to support move-constructible deleters; 20.7.1.2.1 [unique.ptr.single.ctor]:
9 - [...] if [the deleter type]
D
is non-reference typeA
, then the [two-argument constructor] signatures are:unique_ptr(pointer p, const A& d); unique_ptr(pointer p, A&& d);
[...]
12 - Requires: [...]
- [if
d
is a non-const rvalue then]D
shall satisfy the requirements ofMoveConstructible
(Table 20), and the move constructor ofD
shall not throw an exception. Thisunique_ptr
will hold a value move constructed fromd
. [...]
If we explicitly add a move constructor and delete the copy constructor from LockDeleter
then we get a more informative error message; http://rextester.com/XFYUG91939:
Error(s):
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\memory(1243) : error C2280: 'LockDeleter::LockDeleter(const LockDeleter &)' : attempting to reference a deleted function
source_file.cpp(10) : see declaration of 'LockDeleter::LockDeleter'
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\memory(1241) : while compiling class template member function 'std::_Unique_ptr_base<_Ty,_Dx,false>::_Unique_ptr_base(int *,_Dx)'
with
[
_Ty=int
, _Dx=LockDeleter
]
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\memory(1380) : see reference to function template instantiation 'std::_Unique_ptr_base<_Ty,_Dx,false>::_Unique_ptr_base(int *,_Dx)' being compiled
with
[
_Ty=int
, _Dx=LockDeleter
]
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\memory(1331) : see reference to class template instantiation 'std::_Unique_ptr_base<_Ty,_Dx,false>' being compiled
with
[
_Ty=int
, _Dx=LockDeleter
]
source_file.cpp(20) : see reference to class template instantiation 'std::unique_ptr<int,LockDeleter>' being compiled
Note the mention of std::_Unique_ptr_base<_Ty,_Dx,false>::_Unique_ptr_base(int *,_Dx)
; this indicates that the deleter parameter is incorrectly being copied into the internal base class, when it should be being moved.
The only workaround I can see is to make lock_
mutable, allowing a copy constructor to operate as a move constructor.