I would say that you should never use in for function parameters. in is an artifact from D1 that was reduced to reducing code breaks, but was changed to the equivalent of const scope . So, every time you think about entering in a function parameter, think about const scope , as that is what you are really doing. And scope is currently only doing something with delegates, and in this case, it tells the compiler that the function receiving the delegate is not going to return it or assign it anything, and therefore there should not be a closure for the state to delegate this ( so that it increases efficiency in many cases for delegates), while for all other types it is completely ignored, which means that using it is pointless (and potentially confusing), and if it ever means something different for others (e.g. about proposed that he provided to the pointer, which is transmitted as scope , could not escape function), then the semantics of your code may change in unexpected ways. Presumably, it will be accompanied by appropriate warnings when this happens, but why do you mark your code with a meaningless attribute that might make sense later and thus force you to change your code? At this point, scope should only be used for delegates, so in should be used only for delegates, and you usually don't need const delegates. Therefore, just do not use in .
So, ultimately, you are really asking whether to use const or const ref .
The short answer is that you usually shouldn't use ref unless you want to mutate the argument you pass. I would also like to point out that this question is meaningless for any structures, and maybe for static arrays, because classes are already reference types, and none of the built-in types (except for static arrays) require significant copying costs. Longer answer ...
Movement semantics are built into D, so if you have a function that takes its argument by value - for example.
auto foo(Bar bar) { ... }
then it will move the argument if possible. If you pass it the lvalue value (the value that may be on the left side of the task), then this value will be copied, except, perhaps, in circumstances where the compiler can determine that it can optimize copying (for example, when a variable never used after calling this function), but it depends on the compiler and compiler flags used. Thus, passing a variable to a function by value usually results in copying. However, if you pass rvalue functions (values ββthat cannot lie on the left side of the task), then it moves this object, rather than copying it. This is different from C ++, where move semantics were not introduced before C ++ 11, and even then they need move constructors, while D uses postlbit constructors that change it, so move can be done by default. A few previous SO questions about this:
Does D have something similar to the C ++ 0x move semantics?
Questions about the semantics of postblit and move
So yes, there are cases in D where passing ref could avoid copying, but in D, ref always requires an lvalue (even with const ). So, if you start to place ref const(T) everywhere, as you would do const T& in C ++, you will have many functions that are really annoying to call, because each temporary should be assigned to a variable first to call the function. Therefore, you should seriously think only about using ref for those cases when you want to mutate a variable that has passed, and not for efficiency. Of course, the default should not pass const ref , but if you need this additional efficiency, you have two options:
- Overload the function with
ref -ness so that you have an overload that is taken with const ref and the other with ref , so lvalues ββare passed to one without copying, and rvalues ββare passed to the other without the need for an extraneous variable. eg.
auto foo(const Bar bar) { foo(bar); } auto foo(ref const(Bar) bar) { ... }
And this is a little annoying, but works pretty well when you only have one parameter with ref . However, you get a combinatorial burst of congestion as you add ref parameters. eg.
auto foo(const Bar bar, const Glop glop) { foo(bar, glop); } auto foo(ref const(Bar) bar, const Glop glop) { foo(bar, glop); } auto foo(const Bar bar, ref const(Glop) glop) { foo(bar, glop); } auto foo(ref const(Bar) bar, ref const(Glop) glop) { ... }
So it works to a certain extent, but it is not particularly nice. And if you define overloads, as I did here, then this also has the disadvantage that rvalues ββare eventually passed to the wrapper function (adding an extra function call - although this should be quite permeable), which means that they are now ref is passed to the main overload, and if one of these parameters is passed to another function or returned, the compiler cannot perform the move, whereas if ref not involved, it could have. This is one of the reasons why it is now argued that you shouldn't use const T& heavily in C ++ 11, as it would in C ++ 98.
You can work around this problem by duplicating the function body for each overload, but this obviously creates a maintenance problem and also creates a bloat code.
- An alternative is to use
auto ref , which basically does it for you, but the function needs to be templated. eg.
auto foo()(const auto ref Bar bar, const auto ref Glop glop) { ... }
So, now you only have one overload, but it still generates all these overloads with full code under the hood every time a template is created using another combination of ref -ness. This way, your code is cleaner, but you still get more bloat, and if you need to do this using a virtual function, then you are out of luck and need to return to a more explicit solution to overloading, because template functions canβt be virtual.
So, in general, trying to get your const ref functions for efficiency reasons just gets ugly. The fact that D has built-in move semantics reduces the need for it (as with C ++ 11, he now argued that passing by value is often better, thanks to the move's semantics and how the compiler optimizes them). And it's ugly enough to do in D in the general case that if you really don't get a performance boost that matters, you probably shouldn't go past ref just for efficiency. You should probably avoid using ref for efficiency unless you have measured the difference in performance that is worth the pain.
Another thing to consider - from ref -ness - is that D const is much more restrictive than C ++ const (for example, dropping const and mutation is undefined behavior in D, and D const is transitive). Thus, patting const all over the place can sometimes become problematic - especially in general code. Thus, using it can be great to prevent accidental mutations or to indicate that a function does not mutate its arguments, but not just blithely tickle it everywhere, which should not mutate a variable, as in C ++. Use it where it makes sense, but keep in mind that you will come across cases where D const is too restrictive to be useful, even if C ++ const would work.
So, in most cases, when you want your function to take T , you should use plain T by default. And then, if you know that efficiency is a problem, you can use some form of ref (perhaps preferring auto ref or const auto ref if you are not dealing with a virtual function). But by default, do not use ref . Thus, your life will be much more pleasant.