这是我经常遇到的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
添加额外状态?
答案 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;不可移动的。