Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RAII and deduced template arguments

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?

like image 638
HighCommander4 Avatar asked Jul 14 '14 18:07

HighCommander4


People also ask

What is template argument deduction?

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.

What are template arguments enlist types of template arguments?

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).

What is the difference between Typename and class in template?

There is no difference between using <typename T> OR <class T> ; i.e. it is a convention used by C++ programmers.

How do you use template arguments in C++?

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.)


2 Answers

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);
}
like image 123
nosid Avatar answered Sep 20 '22 00:09

nosid


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.

like image 22
Stian Svedenborg Avatar answered Sep 22 '22 00:09

Stian Svedenborg