36

If some function f with parameters p_1, ..., p_n of types T_1, ..., T_n respectively is called with arguments a_1, ..., a_n and its body throws an exception, finishes or returns, in what order are the arguments destroyed and why? Please provide a reference to the standard, if possible.

EDIT: I actually wanted to ask about function "parameters", but as T.C. and Columbo managed to clear my confusion, I'm leaving this question be about the arguments and asked a new separate question about the parameters. See the comments on this question for the distinction.

Community
  • 1
  • 1
jotik
  • 17,044
  • 13
  • 58
  • 123
  • 1
    I don't know the order, but I guess the answer to the second question is "because the standard says so"... – zmbq May 02 '16 at 21:36
  • 2
    I don't think there is a pre-defined order (same as when you invoke a function with multiple params), but I'd be happy to see an answer quoting the standard. Good question, +1. – vsoftco May 02 '16 at 21:37
  • @MikhailGenkin I could imagine a case in which later parameters reference earlier ones and where code would behave differently depending on the order in which the argument destructors fired. – templatetypedef May 02 '16 at 21:40
  • @zmbq If the standard has something to say on this issue, I'm interested in why they chose that specific approach. – jotik May 02 '16 at 21:40
  • 1
    http://wg21.link/cwg1880. This appears underspecified. – T.C. May 02 '16 at 21:41
  • I don't think this is "defined" (and T.C. seems to agree!) – Mats Petersson May 02 '16 at 21:41
  • @templatetypedef It might be due to the lack of experience, but I cannot imagine this case. Once function evaluation is done, all parameters are destroyed before proceeding to other operations. – Mikhail Genkin May 02 '16 at 21:44
  • @T.C. Parameter objects != arguments. Arguments are, if temporaries, destroyed as usual. – Columbo May 02 '16 at 21:54
  • @Columbo Hmm, it doesn't make much sense to talk about destruction of "arguments", so I assumed that OP is talking about the parameter objects. After all, the arguments need not be objects at all. – T.C. May 02 '16 at 22:00
  • @T.C. It makes much less sense to call parameters arguments. And parameters need not be objects, either. And what would be wrong about reasoning about the lifetime of arguments in some expression? – Columbo May 02 '16 at 22:05
  • 1
    @Columbo Not really. I see people mixing them all the time. And because the lifetime of arguments isn't necessarily tied to the exit from the function - e.g. `void foo(std::string); std::string s; foo(s);` – T.C. May 02 '16 at 22:08
  • @T.C. Sorry, I don't know the exact terminology. I wanted to ask if one has `f(T v, T2 v2) {}` then in what order are `v` and `v2` destroyed. Amended my question respectively. – jotik May 02 '16 at 22:09
  • The answer, as per the link posed by T.C: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1880 - it's not specified, and up to the compiler vendor to "do whatever they think makes sense" (and if they are nice, they'll document it [ideally outside of the compiler sourcecode]) – Mats Petersson May 02 '16 at 22:11
  • 1
    @Columbo Please help me get this straight. So an argument is something passed TO the function, and a parameter is the variable accessible INSIDE the function? – jotik May 02 '16 at 22:18
  • @Columbo Thank you. I hope my edit will help clear up the mess I've made. – jotik May 02 '16 at 22:25
  • 5
    @Columbo I don't think I've ever encountered anyone who was that pedantic about the distinction. Most people I know use parameter and argument interchangeably `main` has `argv` and `argc`. not `paramv` and `paramc`. – Rob K May 03 '16 at 14:44
  • This distinction is sometimes called "formal argument" (variable that captures what is passed) vs "actual or effective argument" (value passed) – Jean-Baptiste Yunès May 13 '16 at 17:33
  • @RobK That's because `main`'s parameters designate the arguments passed in (being a pointer and a scalar describing a property of the pointee of the second argument). And making a point by some historical naming of function parameters is pretty unconvincing. Either way, basic terminology is not too much to ask for, is it, especially when posing a question that changes significantly in meaning when these terms are swapped? – Columbo May 16 '16 at 12:55
  • @RobK Perhaps the people you know don't arbitrarily interchange them, but talk of arguments and parameters when they actually mean those, respectively? – Columbo May 16 '16 at 12:58
  • See also: [What's the difference between an argument and a parameter?](http://stackoverflow.com/q/156767/3919155) However it seems that usually people think of parameters as things defined by the function as inputs, whereas arguments are what you pass as parameters. – jotik May 16 '16 at 13:43

3 Answers3

22

I did not manage to find the answer in the standard, but I was able to test this on 3 most popular C++ compliant compilers. The answer of R Sahu pretty much explains that it is implementation defined.

§5.2.2/8: The evaluations of the postfix expression and of the arguments are all unsequenced relative to one another. All side effects of argument evaluations are sequenced before the function is entered.

Visual Studio C++ Compiler (Windows) and gcc (Debian)
Arguments are constructed in order reverse to their declaration and destroyed in reversed order (thus destroyed in order of delcaration):

2
1
-1
-2

Clang (FreeBSD)
Arguments are constructed in order of their declaration and destroyed in reversed order:

1
2
-2
-1

All compilers were instructed to treat the source code as C++11 and I used the following snippet to demonstrate the situation:

struct A
{
    A(int) { std::cout << "1" << std::endl; }
    ~A() { std::cout << "-1" << std::endl; }
};

struct B
{
    B(double) { std::cout << "2" << std::endl; }
    ~B() { std::cout << "-2" << std::endl; }
};

void f(A, B) { }

int main()
{
    f(4, 5.);
}
jotik
  • 17,044
  • 13
  • 58
  • 123
Zereges
  • 5,139
  • 1
  • 25
  • 49
14

In §5.2.2[4] N3337 is quite explicit on what happens (online draft):

During the initialization of a parameter, an implementation may avoid the construction of extra temporaries by combining the conversions on the associated argument and/or the construction of temporaries with the initialization of the parameter (see 12.2). The lifetime of a parameter ends when the function in which it is defined returns.

So for example in

f(g(h()));

the return value from the call h() is a temporary that will be destroyed at the end of the full expression. However the compiler is allowed to avoid this temporary and directly initialize with its value the parameter of g(). In this case the return value will be destroyed once g() returns (i.e. BEFORE calling f()).

If I understood correctly what is stated in the standard however it's not permitted to have the value returned from h() to survive to the end of the full expression unless a copy is made (the parameter) and this copy is destroyed once g() returns.

The two scenarios are:

  1. h return value is used to directly initialize g parameter. This object is destroyed when g returns and before calling f.
  2. h return value is a temporary. A copy is made to initialize g parameter and it is destroyed when g returns. The original temporary is destroyed at the end of the full expression instead.

I don't know if implementations are following the rules on this.

jotik
  • 17,044
  • 13
  • 58
  • 123
6502
  • 112,025
  • 15
  • 165
  • 265
  • The parameter objects themselves are not temporaries. – T.C. May 02 '16 at 21:43
  • T.C. they're temporaries in the context of the caller, and named objects in the context of the callee – 6502 May 02 '16 at 21:43
  • Isn't that the compiler is allowed to avoid constructing temporaries for function calls? – jotik May 02 '16 at 21:44
  • @jotik: Yes, but only in the "as-if" rules - in other words, only when the compiler can clearly "see" that the overall behaviour is the same for both the construction and elided construction. If in doubt, constructor is called. – Mats Petersson May 02 '16 at 21:45
  • 3
    "they're temporaries in the context of the caller" {{citation needed}} – T.C. May 02 '16 at 21:46
  • @T.C.: I've added a reference to n3337 where the issue is discussed to some detail. – 6502 May 02 '16 at 21:56
  • @MatsPetersson: the "as-if" rules never buys anything. If you can tell if it has been applied or not then it cannot be applied. It's easier to reason without considering it at all... it has no meaning, actually. – 6502 May 02 '16 at 21:59
  • The only parameters in that example are references. – T.C. May 02 '16 at 22:13
  • @T.C.: why do you think it makes a difference? Even if they were objects they exist conceptually before the function body is entered: the function is receiving them from outside, the caller is responsible for creation and destruction... for the function they're just regular already-existing named objects. – 6502 May 02 '16 at 22:19
  • @6502: While the Standard may not define means by which code could tell when the as-if rule is applied, it does not forbid implementations from exposing their inner working in ways that could reveal such things. The as-if rule makes clear that compilers have no obligation to ensure that programs that examine the inner workings of the generated code won't see anything that differs from a straightforward translation. – supercat May 03 '16 at 05:35
  • @supercat: if you want to talk about the as-if rule in a specific implementation then you are free. In the context of the standard C++ language however the as-if rule is empty because its effect are not observable in any way in standard C++ **by definition**. It's like discussing invisible fairies in a physics book. In my opinion it would have been better leaving it out this logically empty rule from the standard as it doesn't belong to that world and simply adds to the confusion. IMO it makes sense in the chapter about machine code generation of say g++ documentation, not in the standard. – 6502 May 03 '16 at 06:33
  • @6502 Sorry about the confusion I caused mistaking arguments for parameters. Since I asked about the arguments I think the currently striked out part of your answer currently best answers it. See my edit to the question and comments below the question. I think arguments can best be thought of as something one *assigns* to the parameters before calling the function. So talking about arguments in the context of their destruction at some function call expression only makes sense if the compiler decides to use temporaries. Otherwise it's a case of destruction of parameters, and CWG#1880. – jotik May 03 '16 at 07:26
  • 2
    @jotik: unfortunately the C++ fuzzyness about the issue is present anyway. The problem is that temporaries should be destroyed at the end of the full expression, but parameters **may** be destroyed at the end of the call (!). This would theoretically mean that a C++ compiler where the callee destroys the parameters would have to always make a copy from the temporary argument to the parameter so that the callee can destroy the copy but the temporary waits for the end of the function. I don't think that any compiler writer would do that to comply with rules that absurd. I think (hope). – 6502 May 03 '16 at 09:10
  • This doesn't even *address* the question, -1. – Barry May 10 '16 at 13:10
  • @Barry: Sorry? the arguments may be temporaries (destroyed at the end of full expression in reverse order of creation) but may be also built directly into the parameters, that are destroyed when the call returns (that may happen before the end of the full expression). Why do you think this isn't answering the question? – 6502 May 10 '16 at 16:03
12

The order in which the arguments to a function are evaluated is not specified by the standard. From the C++11 Standard (online draft):

5.2.2 Function call

8 [ 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 ]

Hence, it is entirely up to an implementation to decide in what order to evaluate the arguments to a function. This, in turn, implies that the order of construction of the arguments is also implementation dependent.

A sensible implementation would destroy the objects in the reverse order of their construction.

jotik
  • 17,044
  • 13
  • 58
  • 123
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 3
    By "platform dependent" you mean "implementation defined"? – jotik May 02 '16 at 21:52
  • @jotik, yes. "platform dependent" is the colloquial term :) – R Sahu May 02 '16 at 21:54
  • @RSahu But as an example, GCC, a compiler which runs on many platforms, may have the same *implementation defined* behaviour on both Linux and Windows, and the same can go for Clang. The Spec uses the term *implementation defined* to mean *up to the implementers to decide what happens here*; it has very little to do with platform per se. – cat May 02 '16 at 23:08
  • 1
    @cat, we are splitting hairs here. – R Sahu May 03 '16 at 03:13
  • 3
    I think the proper term is in fact _unspecified_. ISO indeed has _implementation defined_ as a category, but that is a stronger requirement : it literally means the implementation must publicly define what choice they made. Unspecified means that the choice could vary from release to release, or even depend on compiler settings. – MSalters May 03 '16 at 09:59
  • @MSalters, Agree. It is good to know the subtle but important difference between unspecified behavior and implementation defined behavior. – R Sahu May 03 '16 at 14:26