1

Consider the following program.

#include<iostream> 
using namespace std; 

void fn(int a, int b)
{
    cout << a;
    cout << b;
}

int main()
{
    int a = 10;
    fn(a++, --a);
    fn(a--, ++a);
    return 0;
}

I don't understand the output I get (gcc 11.2):

9101110

Shouldn't a++ be evaluated first? How can fn then get a 9? Is this undefined behavior or simply "indeterminate"? Did C++17 change in this respect?

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
Aadil Khan
  • 21
  • 3
  • 2
    Does this answer your question? [Compilers and argument order of evaluation in C++](https://stackoverflow.com/questions/621542/compilers-and-argument-order-of-evaluation-in-c) – BoP Feb 06 '22 at 09:10
  • Your program does not compile: iostream is not included, namespace std is not used, and fn is not defined when it is called in main. Additionally, the output you show is not the output the code shown would produce (no spaces are output between the numbers). *What you show us is not what you execute.* Admittedly the errors are easy to fix but suddenly we must guess how your program really looks which makes answering a guess as well, and cumbersome, too. – Peter - Reinstate Monica Feb 06 '22 at 09:24
  • I can't get the answer by the way... – Aadil Khan Feb 06 '22 at 11:35
  • This still does not compile. Why don't you simply copy and paste your working program here!? Do not manually edit anything in the edit field here -- only copy and paste from your editor. – Peter - Reinstate Monica Feb 06 '22 at 14:02
  • @πάντα ῥεῖ This is not a dup with the linked question, at least not in C++20. In 7.6.1.3/8, the standard says: "The initialization of a parameter, including every associated value computation and side effect, is **indeterminately** sequenced with respect to that of any other parameter." It is not **un**sequenced. **Un**sequenced would be UB per 6.9.1/10, but *indeterminately* simply says it's sequenced but we don't know how. – Peter - Reinstate Monica Feb 06 '22 at 14:38
  • I edited the question to (1) make the code compile (finally! ;-) ), (2) point to the relevant tags, (3) address the fact (I think) that the language changed in 2017, and (4) asked more concrete questions about the evaluation order that probably inspired your asking here. If I misrepresented you feel free to edit any way you like. And I also think the question was closed wrongly, or at least the link provided doesn't answer it ("expression lists" -- the `a++, --a` in `fn(a++, --a)`-- are not expressions). – Peter - Reinstate Monica Feb 06 '22 at 15:03

1 Answers1

-2

This is undefined behavior. The order of parameter evaluation is unspecified.

See here.

So this question is irrelevant and code like this should never be used.

The output can be 9 10 11 10 on one compiler, and a totally different value on another compiler (while both compilers remain standard-complaint).

adnan_e
  • 1,764
  • 2
  • 16
  • 25
  • 1
    It's sad how often I've seen this on C/C++ tests in schools and even universities. – adnan_e Feb 06 '22 at 09:14
  • 1
    This question relevance notwithstanding (I find it near-impossible to believe there isn't at least one duplicate somewhere; surely there is), per the language standard the order of function argument evaluation is *unspecified* ; it is *not* undefined. – WhozCraig Feb 06 '22 at 09:25
  • Thanks, you are correct and i have corrected the term. The down vote might have been due to me being a bit harsh with the relevance :) – adnan_e Feb 06 '22 at 09:30
  • @Peter-ReinstateMonica I don't think there's even any UB in this; it's just a really bad idea to rely on because of the unspecified eval order. Each argument *will* be evaluated, the value reaped for eventually passing. The unspecified order is what makes this such a terrible thing to do. A fun (and a bit heated) exchange on a similar question (for completely different inquisitive reasons) can be seen [here](https://stackoverflow.com/questions/621542/compilers-and-argument-order-of-evaluation-in-c). – WhozCraig Feb 06 '22 at 09:33
  • @WhozCraig Of course it is UB -- " If a side effect on a scalar object is un-sequenced relative to another side effect on the same scalar object, the behavior is undefined" – Peter - Reinstate Monica Feb 06 '22 at 09:35
  • Yeah, I know, but i thought C++17 changed that, specifically for function arguments. I admit, haven't been on my keeping-up-with-the-standards, but I was under the impression that was specifically addressed in 17. If you know to the contrary, i'll certainly sit corrected. – WhozCraig Feb 06 '22 at 09:37
  • @WhozCraig [Here](https://stackoverflow.com/q/21670459/3150802) is a nice explanation: Since the instructions for both side effects can even be *interleaved* there are possible unexpected outcomes. – Peter - Reinstate Monica Feb 06 '22 at 09:40
  • @WhozCraig The C++17 change is unknown to me, I spoke in haste and with too much confidence. – Peter - Reinstate Monica Feb 06 '22 at 09:40
  • @WhozCraig [This answer](https://stackoverflow.com/a/46171943/3150802) discussing C++17 proposals seems to indicate that it was assignment which was specifically targeted (and explicitly intuitively sequenced right-to-left) but not other subexpressions. I would have to look it up in the standard but I don't have it here on this 'puter. – Peter - Reinstate Monica Feb 06 '22 at 09:48
  • @WhozCraig so what's the final conclusion about my question i mean how first cout is 9 can you explain it here – Aadil Khan Feb 06 '22 at 11:34
  • @AadilKhan The answer is no, I can't. Only the implementation authors could. – WhozCraig Feb 06 '22 at 12:04
  • @WhozCraig So my standard says (7.6.1.3/8) about the *expression-list* (of a function): "The initialization of a parameter, including every associated value computation and side effect, is **indeterminately sequenced** with respect to that of any other parameter." That is not *unsequenced*, which would be UB if "a side effect on a memory location is unsequenced relative to either another **side effect on the same memory location** or a value computation using the value of any object in the same memory location ..." (6.9.1/10). I must assume that such side effects in arguments are not UB. – Peter - Reinstate Monica Feb 06 '22 at 14:23
  • 1
    @AadilKhan Well, to quote the standard (via the publicly accessible draft): "The initialization of a parameter, including every associated value computation and side effect, is **indeterminately sequenced** with respect to that of any other parameter." It is unclear which expression (in your case: a++ or --a in the first call of fn) is evaluated first. Here, `--a` appears to have been evaluated first, changing `a`'s value to 9 which was then used to initialize `fn`'s `a`. The more interesting question to me is why `fn`'s `b` is 10. – Peter - Reinstate Monica Feb 06 '22 at 14:31
  • @Peter-ReinstateMonica: It’s the initialization of the parameters that is indeterminately sequenced, not the evaluation of the argument expressions. – Davis Herring Feb 06 '22 at 23:52
  • @Davis Oh. I read that as synonymous, or at least as the argument evaluation being a first step -- one of the "associated value computations" -- of the initialization. You suggest initialization here means execution of constructors etc. with respect to possible side effects on other parameter initializations? I'm not sure how to separate the value computation of a constructor (or simple initialization) argument from the actual initialization. – Peter - Reinstate Monica Feb 07 '22 at 00:43
  • @Peter-ReinstateMonica: Yes, initialization includes constructors (and/or conversion functions) needed to convert the final calculated value of an argument (during which its side effects have already occurred and including any functions it calls). The statement about its value computations is saying that a read of a variable inside such a constructor can't see a value that exists only during another such constructor call. – Davis Herring Feb 07 '22 at 04:04
  • @DavisHerring I asked a [separate question](https://stackoverflow.com/q/71013035/3150802) about function call arguments and was pointed to the standard proposal making parameter initialization indeterminately sequenced [here](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r3.pdf). The intent is clearly that specifically the evaluation of the arguments is sequenced ("Every value computation and side effect associated with the initialization of a parameter, and the initialization itself..."). – Peter - Reinstate Monica Feb 07 '22 at 06:11
  • @Peter-ReinstateMonica: You have to be careful reading proposals: that one was adopted **with** the “Alternate Evaluation Order…” (marked with “not to pursue”) applied, and my interpretation is that the wording applied did not reflect the “evaluations of the **arguments** are indeterminately sequenced” (emphasis added) from the prose. – Davis Herring Feb 07 '22 at 19:29
  • @DavisHerring _The statement about its value computations is saying that a read of a variable inside such a constructor can't see a value that exists only during another such constructor call_ What was the point in specifying this for constructors/conversion functions invocations in C++17 parameters initialization when any function invocations, anywhere, couldn't «interleave» since forever? – Language Lawyer Feb 07 '22 at 21:29
  • @LanguageLawyer: I said "constructors" because that was the going subject of conversation; it also applies to conversions requried for those constructors' arguments (which are grouped together here even if they might be several function calls), aggregate initialization effects, _etc._ Obviously there is uncertainty as to what that phrasing means, but that's how I've been interpreting it. – Davis Herring Feb 07 '22 at 21:47