On Wednesday, June 19, 2019 12:28:12 PM MDT XavierAP via Digitalmars-d-learn wrote: > On Wednesday, 19 June 2019 at 12:55:09 UTC, Jonathan M Davis > > wrote: > > Even in C++, using const ref is not as good a practice as it > > once was, because they added move constructors, finally making > > object moveable. The result is that in many cases, it's > > actually more efficient to just copy values in C++ rather than > > use const &, but which is better does depend on the code. > > > > As for D, unless you're dealing with large objects, odds are > > that worrying about passing by value is pointless. D classes > > are reference types, and D structs have move semantics > > built-in. So, you don't get as many copies as you would in > > C++98, and the situation is probably better than newer versions > > of C++, since IIRC, C++ classes aren't moveable by default, > > whereas D structs are. In general, you're probably better off > > just passing by value unless you find that a particular piece > > of code is inefficient when benchmarking. Either way, you don't > > want to be slapping const on everything the way you would in > > C++, because D's const is far more restrictive. So, while it > > still can be quite useful, odds are that if you start using it > > heavily, you're going to run into problems fast - especially > > since casting away const and mutating an object is undefined > > behavior in D. D's const has no back doors. If something is > > const, then you can't mutate it unless you also have a mutable > > reference to the same data. And because const is transitive, > > you pretty much can't get mutable stuff from const stuff like > > you frequently can in C++ (e.g. in C++, it's possible to have a > > const container of mutable objects, wherein D, once part of > > something is const, everything within that part is const). > > > > As for the DIP, I'd suggest watching Andrei's recent dconf talk > > on the subject: > > > > https://www.youtube.com/watch?v=aRvu2JGGn6E&feature=youtu.be > > > > - Jonathan M Davis > > I am not talking about cases that would be candidate for moving, > or where const would be any problem. If you want an example for > the sake of argument: > > struct Matrix3D > { > Matrix3D opBinary(string op)(const ref Matrix3D rhs) const; > } > unittest > { > auto a = Matrix3D.random; > assert(a == a * Matrix3D.identity); > assert(a == a + Matrix3D.zeros); > } > > I did watch Andrei's talk, actually this is where I started and > learned about the DIP(s), then I was confused that 1016 had been > rejected, and smelling that it may be "reopening" I was not sure > where I can find the "index" of DIPs under discussion or > whatever... :)
The DIPs are here: https://github.com/dlang/DIPs Aside from looking through the newsgroup/forum for discussions on DIPs, that's pretty much all you're going to find on that. Andrei's talk is the most up-to-date information that we have about this particular DIP. > > IIRC, C++ classes aren't moveable by default, whereas D structs > > are. > > What do you mean that structs are movable? I know about RVO (in > both D and C++, supposedly guaranteed by all compilers in > practice, but not by language spec -- why not D?), but what about > passing up the stack as here? I mean that the object is moved from one place in memory to another rather than copied. There are cases where the compiler needs to take an object and put it somewhere else. RVO may be one of those, though if I understand correctly with RVO, it may just place the return value outside of the function in the first place to avoid needing to move. But regardless of whether RVO causes a move, there are cases where the compiler can move an object rather than copying it. A very simple case of where a move could theoretically happen (though I'm not sure how much this happens in practice) would be with code like void foo() { MyStruct m; bar(m); } void bar(MyStruct m) { ... } bar takes its argument by value. So, it can't take a reference to avoid a copy. However, because when bar is called, it's the last time that m is used inside of foo, rather than copying m to where bar would use it as a parameter, the compiler could choose to move the object to where bar expects its parameter to be rather than copying it - so it could just blit the bits over. In some cases, it may be able to just place m in the right place that bar will get it without it being copied, but with more complicated code, that's not possible. A far more likely case where objects would be moved is with temporaries. e.g. with this code func(foo(), bar(42), baz("hello")); assuming that none of these functions return by ref, func is taking temporaries from several functions, and the spec actually guarantees that they will not be copied. So, if the compiler needs to move them to put them in the right place for func to get them as parameters, it will. In C++98, it really isn't legal for the compiler to move objects. This is because it's legal to have classes/structs with pointers to any part of themselves (e.g. if an object contained a static array, it could also contain a pointer to one of the elements in that array), meaning that if they were moved rather than copied, such pointers would then point to the wrong place, putting the object in an invalid state. This means that C++98 does a lot of copying that shouldn't be necessary and is part of why const& takes rvalues in C++. C++11 added move constructors to solve that problem. So, now, in C++, if an object has a move constructor, and the compiler wants to move it, it can call the move constructor, allowing for the object to do stuff like fix its internal pointers so that they point to the correct place after the object has been moved. D's solution to this was to just say that having a struct with a pointer to its own internals is not supported, and if you do it, your object will end up in an invalid state whenever it's moved. The result is that the compiler is allowed to move structs whenever it decides that it's a good idea. This obviously isn't good for the rare cases where you really do want an object with a pointer to its own internals, but it's fantastic for the typical case, because it gets rid of the need for a lot of copying. So, unlike C++98, objects can be moved freely, and unlike C++11, there's no need for move constructors. This significantly reduced the need for something like const& in D, and actually, one of the main motivators for the DIP to support passing rvalues by ref is for interacting with C++ code that does it. However, the guys at Weka (who are doing lots of low level stuff in their product because of how high performance it is) came up with a use case where they basically needed move constructors. So, one of the Weka employees wrote a DIP for introducing what is essentially the move equivalent of a postblit constructor. Unlike with C++, it wouldn't be necessary for an object to be moveable, but it _would_ allow for structs to essentially have a move constructor for those cases where the default behavior is not enough (similar to how copy constructors and postblit constructors are not necesary if the default copying behavior is enough but _are_ necessary when it isn't). I don't think that the DIP has been implemented yet, but it _has_ been approved. https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1014.md If you really want to know more about moving, I'd suggest that you read up on move constructors and move semantics. There should be plenty of resources online that talk about the issue in C++, and I expect that a number of them explain it better than I do. The key thing here for D is that all structs are considered to be moveable without the need for any kind of move constructor. - Jonathan M Davis