Evaluation order of function arguments and default arguments
Asked Answered
C

2

20

I recently ran across the following situation:

#include <iostream>

int *p = 0;

int f() {
    p = new int(10);
    return 0;
}

void g(int x, int *y = p) {
    std::cout << y << std::endl;
}

int main() {
    g(f());
}

This is quite subtle, since you usually don't expect the default arguments to change during their evaluation for the function call. I had to take a look at the assembly to spot this error.

Now my question is: Is this really undefined behavior, since there aren't any guarantees concerning the evaluation order of function arguments?

Costermansville answered 26/8, 2015 at 9:9 Comment(0)
I
15

The order of evaluation (i.e. determining the value) of function arguments is not specified. The compiler is free to execute them in any order, and even intermingled if there are no other factors stopping it from doing so.

Evaluation of default arguments happens in the context of the caller, not the callee. So the call to f() is necessary for one argument, and reading the global variable p for the other. Which order this happens in is not specified, so the global could be read before or after the call to f().

Instep answered 26/8, 2015 at 9:19 Comment(3)
"Evaluation of default arguments happens in the context of the caller" Do you have a reference in the current C++ standard for this claim?Costermansville
There's a non-normative note in 1.9p11 that says it explicitly. Other than that, I think it's only implied by lack of distinction from other arguments.Instep
@Costermansville see [dcl.fct.default]/9 "Default arguments are evaluated each time the function is called. The order of evaluation of function arguments is unspecified.", I think this clearly implies that g(f()) is the same as g(f(), p)Feldspar
C
14

If I understand it correctly, your call

    g(f());

is equivalent to

    g(f(), p);

due to the declaration

    void g(int x, int *y = p);

And arguments to the g function , f() and p, can be evaluated in any order, so you can get g called with y assigned either zero (if p is evaluated first, then it returns its initial value) or the newly allocated array pointer (if f() is evaluated first and it assigns a new value to p as its side effect).

Clarion answered 26/8, 2015 at 9:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.