Here's an issue I often run into with RAII. I was wondering if anyone had a good solution for it.
Start with your standard RAII utility class:
class RAIIHelper {
RAIIHelper() {
AcquireAResource();
}
~RAIIHelper() {
ReleaseTheResource();
}
};
Now, for various reasons, I need to make it a template. Let's also say its constructor takes an argument of the template parameter type:
template <typename T>
class RAIIHelper {
RAIIHelper(T arg) {
AcquireAResource();
}
~RAIIHelper() {
ReleaseTheResource();
}
};
Now consider a use site:
void func() {
RAIIHelper<SomeType> helper(someObj);
}
It's annoying to have to write out SomeType
when it can be deduced from someObj
, so I write a helper function to deduce the type:
template <typename T>
RAIIHelper<T> makeRAIIHelper(T arg) {
return RAIIHelper<T>(arg);
}
Now I can use it like so:
void func() {
auto helper = makeRAIIHelper(someObj);
}
Wonderful, right? Except there's a snag: RAIIHelper
is now required to be copyable or movable, and the destructor - which releases the resource - can potentially be called twice: once for the temporary returned by makeRAIIHelper
, and once for the local variable in the calling function.
In practice, my compiler performs RVO and the destructor is called only once. However, this is not guaranteed. This can be seen from the fact that if I try to give RAIIHelper
a = delete
'd move constructor, the code no longer compiles.
I could add additional state to RAIIHelper so that it knows not to call ReleaseTheResource()
after it's been moved-from, but that's extra work that was unnecessary before I added makeRAIIHelper()
to get the type deduction.
Is there a way I can get the type deduction, without having to add extra state to RAIIHelper
?
unique_ptr
there, are you sure there's absolutely no natural sentinel object of typeSomeType
? – Karisaunique_ptr
. – Quincestd::lock_guard<std::mutex> lock(m_mutex);
is evidence that you can't do this without a type that's either movable or copyable. – Streetounique_lock
, say. But it's dangerous:{ auto x = make_lock(mx_); /* ... */ }
is OK, but it's easy to write the erroneous{ make_lock(mx_); /* ... */ }
instead. – Coquette__attribute__((warn_unused_result))
and MS has_Check_return_
. – Karisa