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
?
Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.
Default template arguments are specified in the parameter lists after the = sign. Defaults can be specified for any kind of template parameter (type, non-type, or template), but not to parameter packs (since C++11).
There is no difference between using <typename T> OR <class T> ; i.e. it is a convention used by C++ programmers.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
There is a very simple solution: Use a reference to the temporary object instead of copying it into a local variable.
void func()
{
auto&& helper = makeRAIIHelper(someObj);
}
Building on the previous answers and comments:
You could leave the responsibility of being movable to unique_ptr, and return your resource like this:
template <class T>
auto makeRAII( T arg ) -> std::unique_ptr<RAIIHelper> {
return make_unique(RAIIHelper<T>(arg));
}
Now it scopes like a static variable, but may be uncopyable & unmovable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With