RAII和推导出的模板参数

时间:2014-07-14 18:30:34

标签: c++ templates c++11 raii

这是我经常遇到的RAII问题。我想知道是否有人有一个很好的解决方案。

从您的标准RAII实用程序类开始:

class RAIIHelper {
  RAIIHelper() {
    AcquireAResource();
  }
  ~RAIIHelper() {
    ReleaseTheResource();
  }
};

现在,由于各种原因,我需要将其作为模板。我们还要说它的构造函数接受模板参数类型的参数:

template <typename T>
class RAIIHelper {
  RAIIHelper(T arg) {
    AcquireAResource();
  }
  ~RAIIHelper() {
    ReleaseTheResource();
  }
};

现在考虑使用网站:

void func() {
  RAIIHelper<SomeType> helper(someObj);
}

SomeType可以从someObj推断出来时,必须写出来template <typename T> RAIIHelper<T> makeRAIIHelper(T arg) { return RAIIHelper<T>(arg); } 很烦人,所以我写一个辅助函数来推断出类型:

void func() {
  auto helper = makeRAIIHelper(someObj);
}

现在我可以像这样使用它:

RAIIHelper

太好了,对吧?除了那里有一个障碍:makeRAIIHelper现在需要是可复制的或可移动的,并且释放资源的析构函数可能被调用两次:一次是RAIIHelper返回的临时值,并且一次用于调用函数中的局部变量。

实际上,我的编译器执行RVO并且只调用析构函数一次。但是,这不能保证。这可以从以下事实看出:如果我尝试给= delete ReleaseTheResource()移动构造函数,则代码不再编译。

我可以向RAIIHelper添加其他状态,以便它知道在移动后不会调用makeRAIIHelper(),但在我添加{{1}之前,这是不必要的额外工作获取类型推导。

有没有办法可以获得类型扣除,而无需向RAIIHelper添加额外状态?

2 个答案:

答案 0 :(得分:8)

有一个非常简单的解决方案:使用对临时对象的引用,而不是将其复制到局部变量中。

void func()
{
    auto&& helper = makeRAIIHelper(someObj);
}

答案 1 :(得分:1)

以前的答案和评论为基础:

你可以将责任移到unique_ptr,并像这样返回你的资源:

template <class T>
auto makeRAII( T arg ) -> std::unique_ptr<RAIIHelper> {
     return make_unique(RAIIHelper<T>(arg));
}

现在它的范围就像一个静态变量,但可能是不可复制的&amp;不可移动的。