2014-07-14 103 views
8

这是我经常遇到的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返回的临时值,一次为调用函数中的局部变量。

实际上,我的编译器执行RVO,析构函数只被调用一次。但是,这不能保证。从这个事实可以看出,如果我试图给RAIIHelper一个= delete'd移动构造函数,代码不再编译。

我可以向RAIIHelper添加额外的状态,以便它知道在移动之后不会调用ReleaseTheResource(),但这是在添加makeRAIIHelper()以获得类型扣除之前不必要的额外工作。

有没有一种方法可以得到类型扣除,而不必为RAIIHelper增加额外的状态?

+0

在旁边,看起来你正在重新创造'unique_ptr',你确定绝对没有天生的'SomeType'类型的哨兵对象吗? – Deduplicator

+0

你可以使用'unique_ptr'。 –

+0

编写适当的移动构造函数并禁用复制构造函数。这应该不成问题。 –

回答

8

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

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

如果你用'直接初始化'来替换'return RAIIHelper (arg);'''返回{arg}',那么这个类型不需要是可移动的。 – dyp

+0

当临时对象超出范围时,下一行将如何工作? –

+4

@ StianV.Svedenborg该临时的生命周期延长到范围的末尾。 – dyp

1

大厦:

你可以离开可移动到的unique_ptr的责任,并返回你的资源是这样的:

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

现在,它的作用域像一个静态可变的,但可能是不可移动的,不可复制的&。

+0

这有效,但它强加了不必要的动态分配的开销。 – HighCommander4

+0

根据对象的生命周期和开发者的懒惰(这是一件好事),Aye可能会也可能不值得。 –