Search code examples
c++c++11dynamic-memory-allocationoperator-precedence

Is internal order of execution in function parameters defined?


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?


Solution

  • will memory always be freed upon running this code (no matter what happens) ?

    Before C++17: Nope. One potential order of execution is:

    1. left new Data
    2. right new Data
    3. left unique_ptr constructor
    4. right unique_ptr constructor

    If (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 news 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.