Do I have to pass parameters with ref explicitly?

Ali Çehreli acehreli at yahoo.com
Sun Apr 17 08:45:11 UTC 2022


On 4/16/22 21:56, Elfstone wrote:
 > On Sunday, 17 April 2022 at 04:00:19 UTC, max haughton wrote:
 >> On Sunday, 17 April 2022 at 03:00:28 UTC, Elfstone wrote:
 >>> I'm reading some d-sources, and it looks like they pass big structs
 >>> by value.
 >>> Such as:
 >>>
 >>>     Matrix4x4f opBinary(string op)(Matrix4x4f rhs) { ... }
 >>>
 >>> I came from a C++ background,

Me too. The situation is much simpler compared to C++:

- Classes have no issues with copying at all because they are proper 
reference types. (I argue that classes (types used in OOP hierarchies) 
are reference types in C++ as well but in C++ the programmer must be 
careful to not pass class objects by value. (See the slicing problem.))

- Structs usually don't have issues with copying either because their 
copy is bit-level copying (blitting) by default. In D, structs are value 
types that should not have identities. It is illegal to have struct 
objects that refer to structs or their members by reference. They can be 
moved around at will.

- The latter point happens all the time with rvalues: They are blitted 
around without copying  anything.

Still, the language had (and still has) the post-blit function to give 
the programmer a chance to fix things around when struct objects got 
blitted.

Further still, some industry users (I think it was Weka.io) convinced 
the D community that it was impossible to implement certain idioms (was 
that reference-counting?) without proper copy constructors; so now D has 
copy constructors which are encouraged. (Post-blit are discouraged but 
they take precedence when both the post-blit and the copy constructor 
exist for a type.)

- Somewhat related, unlike C++, D does not allow binding rvalues to 
'const ref'. (This point is relevant for 'in' below.)

 >>> and I would have written:
 >>>
 >>>     Matrix4x4f opBinary(string op)(const ref Matrix4x4f rhs) { ... }

D has always had 'in' parameters but they were nothing other than the 
shorter syntax for 'const scope'. Now, when compiled with the 
-preview=in compiler command line switch, 'in' parameters relieve the 
programmer from some mental load on how to pass parameters. In D, if it 
is an input to a function, then mark it as 'in' and the compiler will do 
some magic:

   https://dlang.org/spec/function.html#in-params

I call it magic because it allows passing rvalues as ref! (I think this 
obviates the "missing" feature of D that should allow binding rvalues to 
const ref. Well, who really needs it when we have -preview=in now?)

It is further magical because 'in' does not pass types that have copy 
constructor, post-blit, or destructor by-value! Wow!

So, this leaves only your concern of blitting big structs. I still don't 
know size of a struct should be considered big when the following two 
costs are compared:

- Blitting bytes has a cost

- Reaching members of structs (which 'const ref' would incur) through a 
pointer has a cost

 >>> I haven't found anything in the docs yet. Will the d-compiler
 >>> optimize them into virtually the same? Can someone give me a reference?
 >>
 >> It's the same as C++.
 >
 > In C++ I would expect the copy to be optimized away, even when passed by
 > value. Do you mean d-compilers (optionally?) do the same optimization 
too?

You can expect at least the same optimizations for D because two major 
C++ and D compilers share the same backend: clang for C++ and ldc for D.

Judging from how Walter Bright, the creator of D, has been a C++ 
compiler writer, to me it is hard to imagine that he would not make D at 
least as much capable as C++ when it comes to optimizations. However, 
dmd, the reference compiler, is known to be less capable in optimizing 
code from both from ldc and gdc (the gcc's D compiler).

Ali



More information about the Digitalmars-d-learn mailing list