Reputation: 1659
Why can't I declare an optional std::lock_guard
and then assign it later? The same thing with an optional string works just fine.
This works:
std::mutex m;
std::optional<std::lock_guard<std::mutex>> lg(m);
But this doesn't: (*)
std::mutex m;
std::optional<std::lock_guard<std::mutex>> lg;
lg = std::lock_guard<std::mutex>(m);
And this doesn't: (**)
std::mutex m;
std::optional<std::lock_guard<std::mutex>> lg;
lg = std::optional<std::lock_guard<std::mutex>>(m);
But this does:
std::string s;
std::optional<std::string> os;
os = std::string(s);
And this does:
std::string s;
std::optional<std::string> os;
os = std::optional<std::string>(s);
(*) Error message:
1>c:\dev\repos\tp_iu2\iu2\pretests\j00010_adaptercal.cpp(51): error C2679: binary '=': no operator found which takes a right-hand operand of type 'std::lock_guard<std::mutex>' (or there is no acceptable conversion)
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.13.26128\include\optional(547): note: could be 'std::optional<std::lock_guard<std::mutex>> &std::optional<std::lock_guard<std::mutex>>::operator =(const std::optional<std::lock_guard<std::mutex>> &)'
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.13.26128\include\optional(320): note: or 'std::optional<std::lock_guard<std::mutex>> &std::optional<std::lock_guard<std::mutex>>::operator =(std::nullopt_t) noexcept'
1>c:\dev\repos\tp_iu2\iu2\pretests\j00010_adaptercal.cpp(51): note: while trying to match the argument list '(std::optional<std::lock_guard<std::mutex>>, std::lock_guard<std::mutex>)'
(**) Error message:
1>c:\dev\repos\tp_iu2\iu2\pretests\j00010_adaptercal.cpp(51): error C2280: 'std::optional<std::lock_guard<std::mutex>> &std::optional<std::lock_guard<std::mutex>>::operator =(const std::optional<std::lock_guard<std::mutex>> &)': attempting to reference a deleted function
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.13.26128\include\optional(547): note: compiler has generated 'std::optional<std::lock_guard<std::mutex>>::operator =' here
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.13.26128\include\optional(547): note: 'std::optional<std::lock_guard<std::mutex>> &std::optional<std::lock_guard<std::mutex>>::operator =(const std::optional<std::lock_guard<std::mutex>> &)': function was implicitly deleted because a base class invokes a deleted or inaccessible function 'std::_Deleted_move_assign<_Base,_Ty> &std::_Deleted_move_assign<_Base,_Ty>::operator =(const std::_Deleted_move_assign<_Base,_Ty> &)'
The solution was to use std::unique_ptr<std::lock_guard<std::mutex>>
instead, so I am only asking out of curiosity.
Upvotes: 0
Views: 162
Reputation: 30931
You can copy or move a string into an optional, but mutex locks are neither copyable or moveable. So we can't use the assignment operator.
We could use a std::unique_lock
, which is moveable, or we could emplace the lock into the optional:
#include <mutex>
#include <optional>
int main()
{
std::mutex m;
std::optional<std::lock_guard<std::mutex>> lg;
lg.emplace(m);
lg.reset();
}
I'd advise against this pattern if you can possibly avoid it. Prefer to use lexical scope (perhaps grouping statements into new blocks) to start and end your lock guards if at all possible.
Upvotes: 2
Reputation: 123114
std::optional<std::lock_guard<std::mutex>>
is not copy assignable because std::lock_guard<std::mutex>
is not copy assignable. This is by design. std::lock_guard
is not meant to be copied or assigned. Its only purpose is to provide a RAII wrapper that locks a mutex on construction and releases it when the scope is left. If you need no RAII wrapper then std::lock_guard
is the wrong wrapper to use. You can either work your way around that or use a type that can be default constructed without a std::mutex
and assigned later to lock a mutex:
#include <mutex>
int main() {
std::mutex mut;
std::unique_lock<std::mutex> lg;
// ... later
lg = std::unique_lock<std::mutex>{mut};
}
Upvotes: 3