Is there a sequence point between a function call returning an object and a method call on that object?
Asked Answered
L

2

15

If I write f(x)->g(args, ...) can I rely on a sequence point after f(x) before the evaluation of args, ...? I can see arguments both ways:

  • §1.9.17 "When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function."
  • On the other hand, the object pointer is implicitly a hidden argument this as if I'd written g(f(x), args, ...) which suggests it's like an argument, and thus unspecified.

The -> operator is not a normal binary operator, since clearly g(...) cannot be evaluated before f(x) like it could if I wrote f(x) + g(...). I'm surprised I can't find some specific statement about it.

Laminated answered 1/3, 2013 at 21:8 Comment(3)
-> is a binary operator? It looks like a unary operator that returns a pointer to some type, if you ignore how it looks.Casals
@Yakk: It is a unary operator, but what is n-ary (of sorts) is T::g that takes an implicit this together with args. The question is whether the expression that yields the implicit this is sequenced before the evaluation of the rest of the arguments to the member function gVlissingen
Looks like it's not.Snyder
P
14

The answer depends on which version of the C++ standard you are using (or your compiler is using).

C++ 2003 5.2.2 p8 said:

The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered. The order of evaluation of the postfix expression and the argument expression list is unspecified.

This means there is not a sequence point between evaluating f(x) and args.

In C++ 2011 the whole concept of sequence points has been replaced (see N1944), and that wording is now just a note:

[ Note: The evaluations of the postfix expression and of the argument expressions are all unsequenced relative to one another. All side effects of argument expression evaluations are sequenced before the function is entered (see 1.9). — end note ]

and 1.9 p15 says

When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [ Note: Value computations and side effects associated with different argument expressions are unsequenced. — end note ]

This says the expression f(x) and the expression args are sequenced before everything in the body of g, but that they are unsequenced relative to each other, which is the same as the C++03 rules but worded differently.

C++14 has the same rules as C++11, but as noted in the comment below, the rules changed in C++17.

C++ 2017 8.2.2 [expr.call] p5 says:

The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.

This means for your example the following steps happen in order:

  • f is evaluated.
  • x is evaluated and the parameters of f are initialized.
  • The function call f(x) is evaluated.
  • f(x)->g is evaluated.
  • args and the other arguments to g are evaluated and the parameters of g are initialized (in an unspecified order).
  • Finally, the function call f(x)->g(args, ...) is evaluated.
Preparator answered 1/3, 2013 at 22:36 Comment(1)
This has changed in C++17: "14) In a function-call expression, the expression that names the function is sequenced before every argument expression and every default argument." en.cppreference.com/w/cpp/language/eval_order So now, f(x) will evaluate before the args.Franciscofranciska
F
4

Note, I think that you're asking one question in your title, and another in the body of your question.

Well, it's not really contradictory. To evaluate your function, the following things have to happen (not necessarily in this order).

  • x is evaluated (A)
  • args is evaluated (B)
  • ... is evaluated (C)
  • f(x) is called (D)
  • the return value of f(x) is copied (E)
  • return->g(args, ...) is called (F)

Now, the rules you've quoted indicate that

  1. (A) has to happen before (D), since there is a sequence point of evaluating the arguments to a function prior to evaluating.
  2. (D) happens before (E), since the copy can't be made until the function runs.
  3. (F) happens after (E), since the implicit pointer is necessary to invoke g(args) *
  4. (B) and (C) happen before (F), since they are arguments.

However, what is unsequenced is the relationship between (A), (B), and (C), or in your question between (B) and (C) and (D), since they aren't arguments to (F), they could be evaluated afterwards. OR, they could be evaluated prior.

* Interesting question. What happens if g(args, ...) is a static member function. In this case, since the returned pointer from f(x) isn't actually passed in, can it be sequenced earlier? But that's a separate question.

Frediafredie answered 1/3, 2013 at 22:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.