Let's assume we have the following snippet of code:
void foo(std::unique_ptr<Data> p1, std::unique_ptr<Data> p2){
bar(p1);
bar(p2);
}
int main(){
foo( std::unique_ptr<Data>{new Data}, std::unique_ptr<Data>{new Data});
}
The question is: will memory always be freed upon running this code (no matter what happens) ?
Standard says that we cannot rely on order of statements being function arguments but what about the internal function calls/memory allocations etc. ? Is it even important in here?
will memory always be freed upon running this code (no matter what happens) ?
Before C++17: Nope. One potential order of execution is:
new Data
new Data
unique_ptr
constructorunique_ptr
constructorIf (1) throws, we're ok. If (2) throws, we haven't actually run the unique_ptr
constructor yet, so we don't have any cleanup mechanism to free the memory from (1). That will leak. Only in the case that neither (1) nor (2) throw are we fine.
This is why the standard introduced std::make_unique
:
foo( std::make_unique<Data>(), std::make_unique<Data>() );
which doesn't have this problem - the new
s are now internally grouped to the unique_ptr
constructors. So if one succeeds, it already will have been wrapped in its RAII guard.
After C++17: Yes. While order of evaluation of function arguments is still unspecified, there is a new rule that such evaluation cannot be interleaved. That is, given f(g(a), h(b))
, the potential orders of evaluation are: [f, g, a, h, b]
and [f, h, b, g, a]
. It is no longer possible to evaluate both a
and b
before g
or h
, which was the potential problem with the original code.