Note: parameter passing by xxx is sometimes called "pass by xxx" and
sometimes "call by xxx". I will use both expressions indifferently.
Parameter passing by value-result consists in passing by value when the
subprogram (whatever it is) is called, and then, when it terminates,
assign the current value of the formal parameter in the subprogram
back to the actual parameter if it is a mutable entity, such as a
variable or an array entry. Of course, no such assignment may occur if
the actual parameter is a non mutable value providing entity such as
an expression, a function call, a constant (though some old versions
of Fortran have been known to assign values to constants, so that
printing 3 could actually print 5).
Afaik, fortran was first to use this mechanism (I do not know for
Cobol). It is also available in Ada. Actually, Ada has call by value
(IN parameter) and call by result (OUT parameter). OUT parameters are
not initialized at call, but assign current value of formal parameter
to the actual parameter upon return. Parameter passing by value-result
(IN OUT) is the combination of both. Actually, the case of Ada is more
complicated (at least in its initial versions) because it was
explicitly allowed to implement these parameter passing mechanism with
a call by reference. I am not sure of the current status of the
language. This of course muddles the discussion below.
Comparing parameters passing mechanisms is dependent on the language
considered.
Issues you may want to consider are cost in time or space, readability
and maintenance, compiling issues such as efficiency and optimization,
and also semantics such as determinacy of programs, and also programming
convenience. (I may have forgotten some).
Many of these issue are stongly impacted by aliasing
phenomena. Whenever the same mutable entity (possibly a simple
variable) can be accessed in different ways, it becomes harder to
analyse the program, determine what parts of the computation may
interfere with other parts. When there are several threads of
computation, this may cause race conditions making the computation
dependent on timing. When compiling program, aliasing may make it harder
to get a reliable definition-use relation for performing some
otpimizations.
Hence,I would consider aliasing problems a major issue when comparing
computing mechanisms.
Since you need ways to communicate in programs, assessing a parameter
passing mechanism can only by comparing to alternatives.
Regarding pass-by-value-result, the are only two that I see:
Doing nothing on exit.
Then you can pass information back to the caller only as function
result, or by assigning a global variable visible from both callee
and caller.
Using pass by reference.
Then the formal parameter becomes just
another name, an alias, for the actual parameter, so that the actual
parameter has the value of the formal one upon exit (as well as
before exit).
Issues
Time and space cost
One advantage of call by reference over call by value-result is that
it is often cheaper, especially if the data to be passed is large
(time/space costs of copying). Call by reference may have a time cost
because of indirect addressing, though that can sometimes be optimized
out.
Aliasing
The major drawback of call by reference is all I said about
aliasing. So that may be considered a very good point for
pass-by-value-result.
Side effects and evaluation order
Then call by value-return has a problem when returning the values, as
in your example. If two formal parameters (x and y in the example)
actually get the same actual parameter (b in the example) then the
actual parameter will take on return a value that depends on the order
chosen for returning values. Hence, it is essential to define it. In
any case, such a situation is a sure indicator of bad programming
practice.
This problem does not exists with call by reference. But call by
reference is a completely different kind of computation, as shown by
executing your example in call by reference.
Returning values is a side-effect on the actual
parameters. Computations with side-effects always raise issues of
order dependence. If the evaluation of actuall parameters entails some
side-effects (for example by calling a function with side-effects),
then the order of evaluation of arguments may have an impact on the
computation. That is why side-effect are to be avoided, or at least
carefully managed.
Objects identity
If you have a language that allows to distinguish identity and
egality, the call by reference wil preserve identity, while call by
value will not. For example, when you copy an integer (for example by
assignment), there is no way you can distinguish the new copy from the
old one. But Object-Oriented (OO) languages could let you make a
difference, and have a test for equality (same value) and a test for
identity (same reference).
Well definedness of call by value (irrespective of result)
Another problem is that call by value may not be well defined. You
have to determine what a value is. How deep do you copy an object
implemented with references (pointers) when doing call by value.
When a reference is not followed for copying, it is an open door for
aliasing problems.
I am not saying these issues are untractable, but only that you have
to deal with them consistently throughout the language (consistency
with assigment in particular). And that does not make for a clearcut
answer, independently of the rest of the language, and the style of
programming it supports.
Running the example in call by value-result and call by reference
Notation
a<==b a becomes aliased to b
a==b a is alias of b (reminder)
a<-3 a is assigned the value 3
Call by value-result:
Call of fun(b,b): x<-1 y<-1 b<-2 x<-3 y<-5 return print 2 (3 or 5)
unpon returning, b ends up with 3 or 5 depending on the order of argument return.
Call by reference:
Call of fun(b,b) x<==b y<==b b==b<-2 b==x<-4 b==y<-8 return print 2 8