On Monday, 29 June 2015 at 19:10:07 UTC, Atila Neves wrote:
On Monday, 29 June 2015 at 17:19:48 UTC, Jonathan M Davis wrote:
On Sunday, 28 June 2015 at 10:50:10 UTC, Marc Schütz wrote:
Thank you Jonathan, I think I (like kinke in his post above) finally understand your stance. So I guess from your POV the following would be an ideal situation (disregarding the need to avoid breaking changes):

1) `auto ref` for non-templates is required to make a function accept rvalue and lvalue refs alike. 2) `auto ref` for templates is changed to mean the same thing as 1).
3) `scope ref` prevents escaping of the reference.

Neither of `auto ref` and `scope ref` would imply the other, because you want to treat them as orthogonal. Do I understand you right?

The main problems with this is that they aren't in fact orthogonal: `auto ref` without `scope ref` almost only makes sense if you want to deliberately break something. Furthermore, `auto ref` is already taken for something else, and I'm not sure Walter would be too happy if we wanted to change its meaning for templates.

We definitely don't want to lose the current auto ref that we have with templates. It's critical for forwarding refness, even if we got that ability by accident. IIRC, we don't quite manage perfect forwarding with what we have (though off the top of my head, I don't recall what we're missing), but without auto ref, we definitely don't get there. So, auto ref, as it stands has proven to be surprisingly useful completely aside from efficiency concerns.

I thought perfect forwarding had everything to do with efficiency concerns. At least, that's the motivation for in in C++ AFAICT/AFAIR. If not, (since C++ const& can bind to rvalues), passing by value would be perfectly reasonable.

I don't remember. It's been a while since I looked at it, so I'd have to look it up again to know quite what it is or exactly why it's needed. I do remember that without auto ref though, we're definitely not there.

It seems to me that there's a lot of confusion in this thread. I think I understand what you're saying but I'm not sure everyone else does. So correct me if I'm wrong: your stance (which is how I understand how things should be done in D) is that if a person wants to bind an rvalue to a ref parameter for performance, they shouldn't; they should pass it by value since it'll either get constructed in place or moved, not copied.

Essentially, what this guy said about C++ (not the original I read, that's now a 404):

http://m.blog.csdn.net/blog/CPP_CHEN/17528669

The reason rvalue references even exist in C++11/14 is because rvalues can bind to const&. I remember hearing/reading Andrei say that D doesn't need them precisely because in D, they can't. Pass by value and you get the C++ behaviour when rvalue references are used: a move.

Yeah. In general, it's more efficient to pass rvalues by value, because the compiler can avoid copying them and simply move them (and at least some of the time, it doesn't even need to move them, though I don't know enough about how stuff is implemented at that level to know whether it can always avoid even having to move rvalue arguments; it can certainly avoid copying them though). If you pass in an rvalue by reference (via const& in C++ or by the proposed non-templated auto ref in D), then you actually increase the odds that the argument will have to be copied. And if no copy is needed anywhere, then I believe that passing an rvalue by ref and passing it by value end up being more or less equivalent. So, the end result is that it's not actually beneficial to pass an rvalue by reference rather than by value.

The problem that you run into is that if you want a parameter to accept both lvalues and rvalues, then the more efficient thing for rvalues is to pass by value, whereas the more efficient thing to do for lvalues is to pass by reference, and const& in C++ forces you to pass by ref, whereas passing by value... forces passing by value. The same would go with the non-templated auto ref. However, the templated auto ref avoids that by inferring the refness of the argument and thus automatically accepts rvalues by value and lvalues by reference. So, you end up passing arguments efficiently regardless of whether they're lvalues or rvalues; it's just that it costs you a lot of template bloat in the process.

But a lot of programmers are used to simply using const& everywhere in C++, because prior to C++11, rvalues couldn't generally be moved, because there were no move constructors, and C++ doesn't disallow stuff like having a class/struct on the stack having a pointer to one of its members (which D does disallow - or at least assumes you won't do), and so the best way to avoid extra copies was to ensure that you passed by const& as much as possible. But with C++11, the situation becomes much more complicated, and figuring out what the most efficient thing to do is not as straightforward. And AFAIK, C++ has nothing like our templated auto ref that allows you to efficiently accept both lvalues and rvalues (but maybe that's where perfect forwarding comes in; I don't know; I really need to read up on it), leaving you to figure out whether having a function take its arguments by value or const& is more efficient based on what your function does.

Ultimately, it really doesn't seem like there's a simple rule of them that is going to give you efficiency in general. You need to understand the various pros and cons of how arguments are passed if you want to eke out extra efficiency.

Though in general, if you simply avoid having particularly large structs on the stack, I would think that just passing arguments by value as the default would be the best way to go and worrying about how to pass more efficiently when profiling shows that it's necessary. But having auto ref for non-templated functions will give us another tool in the toolbox for adjusting how function arguments are passed in code that cares.

Of course, if you have large structs for whatever reason, that makes the situation more complicated, but the best way to handle that will likely depend on your code.

The only real question I see is whether it's worth using a new attribute (as opposed to auto ref) so that we can use it with templated functions as well, allowing folks to pass rvalues by reference with templated functions if they need to avoid the template bloat or it actually turns out that that's more efficient in that case. As I understand it though, it's pretty much always more efficient to pass rvalues by value. And if that's indeed the case, the only real gain that I see by using a new attribute is that it allows us to avoid template bloat when it needs to be avoided.

- Jonathan M Davis

Reply via email to