Jump to page: 1 2 3
Thread overview
Does D really need something like const&?
Mar 01, 2013
Namespace
Mar 01, 2013
Namespace
Mar 01, 2013
Namespace
Mar 01, 2013
Namespace
Mar 02, 2013
Era Scarecrow
Mar 02, 2013
Era Scarecrow
Mar 02, 2013
Era Scarecrow
Mar 02, 2013
Namespace
Mar 02, 2013
Namespace
Mar 02, 2013
deadalnix
Mar 02, 2013
Namespace
Mar 02, 2013
deadalnix
Mar 02, 2013
Namespace
Mar 02, 2013
Era Scarecrow
Mar 02, 2013
Namespace
March 01, 2013
Currently small structs are moved as rvalues and copied as lvalues and that seems like the most performant way.
But for massy structs this doesn't make sense, if you must pass them to functions as parameter, without declaring the parameter as 'ref'.
Why? Because in general this is very unperformant and that is mostly not what you want.
So I thougth like many other still do: something is missing here, we need something like const&.
But: why do we need something like that?
The more important question is: why is our massy struct a struct and not a class?
Does it make sense that it isn't a class? If we could answer this question generally with No or if we don't have any important reason why the massy struct should stay a struct and not a class, then we do not need something like const&.
If your data is massy: use a class. If not and you don't need polymorphism: use a struct. Done! ... or not?
We should ask us:
1. How could we answer the questions above?
2. Do we really need something that takes both, rvalues and lvalues, if it isn't a template?
I'm really interested to hear what is your opinion.
March 01, 2013
On Fri, 01 Mar 2013 16:16:46 -0500, Namespace <rswhite4@googlemail.com> wrote:

> If your data is massy: use a class. If not and you don't need polymorphism: use a struct. Done! ... or not?

Size is not a major factor when I decide whether to use a struct or class.  I don't think the decision for class or struct should be inextricably linked to data size.

And I believe, actually, that passing a massive struct by value if it's an rvalue IS the most performant -- no copy needed, no referencing needed.

However, I agree we need SOMETHING to avoid writing every function at least twice.

I'd also point out that D's notion of rvalue vs. lvalue is too limited, and results in unnecessary restrictions.  We may need a marker to tell the compiler whether a type is an rvalue or not.  I'd also like to have a way to specify a struct member function takes it's 'this' parameter by value.

-Steve
March 01, 2013
Size is next polymorphism my main reason why I would use a class instead of a struct.
A good heuristic size (which I have heard here) was: <= 16 bytes -> struct, > 16 bytes -> class.
And what are your reasons for decision?

> And I believe, actually, that passing a massive struct by value if it's an  rvalue IS the most performant -- no copy needed, no referencing needed.

And what a massive struct do you think?
For example, if you have something like this:

struct Massy {
public:
	int[1024] marr;
}

I would bet that it is usually better to take 'Massy' by ref, or to use a class, instead of a move or a copy.
March 01, 2013
On Fri, 01 Mar 2013 17:20:00 -0500, Namespace <rswhite4@googlemail.com> wrote:

> Size is next polymorphism my main reason why I would use a class instead of a struct.
> A good heuristic size (which I have heard here) was: <= 16 bytes -> struct, > 16 bytes -> class.
> And what are your reasons for decision?

Features of classes vs. structs.  Need an interface?  class.  Need fine control over lifetime?  struct.

There are lots of other features that can make the decision.  Size is a very small part of it.

>
>> And I believe, actually, that passing a massive struct by value if it's an  rvalue IS the most performant -- no copy needed, no referencing needed.
>
> And what a massive struct do you think?
> For example, if you have something like this:
>
> struct Massy {
> public:
> 	int[1024] marr;
> }
>
> I would bet that it is usually better to take 'Massy' by ref, or to use a class, instead of a move or a copy.

foo(Massy m)
{
   for(int i = 0; i < sizeof(m.marr); ++i)
     m.marr[i] = i;
}

main()
{
   foo(Massy());
}

OK, so let's take the case that foo accepts Massy by ref:

1. Massy has to be pushed onto the stack
2. A ref to Massy is pushed onto the stack
3. Every access to m in foo must go through a pointer dereference (there are 1024 of them)

What if Massy is accepted by value?

1. Massy has to be pushed onto the stack
2. Massy is NOT copied to pass to foo, it's passed directly, there is no copy involved!  No ref is needed either
3. Every access to m does NOT need to be dereferenced.

I agree, lvalues, Massy needs to be passed by ref.  But the case can be made that passing rvalues by ref is lower performance, even if slightly.

Often times though, I would think that it's not worth the trouble to have both a ref and non-ref foo.  In that case, passing foo by ref is preferred, because the penalty is only slight in passing an rvalue by reference, but is humongous when passing an lvalue by value.

auto ref was supposed to be this magic feature.

-Steve
March 01, 2013
I know about 'auto ref'. Nice try but ... ;)

> Need fine control over lifetime?
scope and scoped give you the possibility to put a class instance on the stack so you have also controll over the instance lifetime.
-> No reason for struct.
Interfaces, well, but I'm sure you can live without them sometimes.
March 01, 2013
On Fri, 01 Mar 2013 18:05:12 -0500, Namespace <rswhite4@googlemail.com> wrote:

> I know about 'auto ref'. Nice try but ... ;)

Nice try?  I don't get this.  It was supposed to be the analogue to C++ rvalue references, Walter did not implement it as Andrei expected (AIUI).  He made it a (admittedly useful) template feature.

>> Need fine control over lifetime?
> scope and scoped give you the possibility to put a class instance on the stack so you have also controll over the instance lifetime.
> -> No reason for struct.

scoped is implemented via a struct...

> Interfaces, well, but I'm sure you can live without them sometimes.

OK, so by eliminating my use cases that are problematic for your theory, yes, I guess I can base all my decisions on size ;)

-Steve
March 01, 2013
> Nice try?  I don't get this.  It was supposed to be the analogue to C++ rvalue references, Walter did not implement it as Andrei expected (AIUI).  He made it a (admittedly useful) template feature.

I got the answer that 'auto ref' is not the solution which will solve the const& issue and because of that that,  it will probably never be implemented for non-template functions. So first of all they must find a new suitable solution for that.

> scoped is implemented via a struct...
Whatever. The fact is that you can put a class instance on the stack and controll the instance lifetime also.

>> Interfaces, well, but I'm sure you can live without them sometimes.
>
> OK, so by eliminating my use cases that are problematic for your theory, yes, I guess I can base all my decisions on size ;)
>
> -Steve
:D My statement was ironic. In this I am not so good, sorry.
It would be very cool to have interfaces (and maybe polymorphism) for structs also.

By the way: My intention was not to start a new flame war about the implementation of something like const&.
There are enough topics and discussions here about that, and I'm sure, that in the next few years a few more will be added.
My intention was to hear other opinions / answers to my questions. So I'm hoping that other express their opinion, too.

At least I have noticed so far, that you're in any case for something like const&. Accordingly, you answer my question with 'yes, we need something like this.'.
March 01, 2013
On Fri, 01 Mar 2013 18:35:43 -0500, Namespace <rswhite4@googlemail.com> wrote:

> At least I have noticed so far, that you're in any case for something like const&. Accordingly, you answer my question with 'yes, we need something like this.'.

I would say yes, we need something like rvalue references to avoid copy-paste hell.  const& is not a good way to describe it, because it implies const, which this problem does not require.

This is the major problem that Andrei had with it (at least as I understand his past statements) -- it conflates const with rvalue references.  Sometimes, you want a const ref that does NOT bind to an rvalue.

The one huge problem I've had with lack of rvalue references is with arithmetic operators:

struct M
{
  M opAdd(const ref M other) const {...}
}

M m;

auto m2 = (m + m) + m; // ok!
auto m3 = m + (m + m); // error!

This is crap.

-Steve
March 02, 2013
> On Fri, 01 Mar 2013 18:35:43 -0500, Namespace <rswhite4@googlemail.com> wrote:
>> At least I have noticed so far, that you're in any case for something like const&. Accordingly, you answer my question with 'yes, we need something like this.'.

On Friday, 1 March 2013 at 23:46:36 UTC, Steven Schveighoffer wrote:
> I would say yes, we need something like rvalue references to avoid copy-paste hell.  const& is not a good way to describe it, because it implies const, which this problem does not require.
>
> This is the major problem that Andrei had with it (at least as I understand his past statements) -- it conflates const with rvalue references.  Sometimes, you want a const ref that does NOT bind to an rvalue.

 If I have a medium sized struct that I only intend to read/reference from I see no reason not to use 'const ref' for simplicity and speed (and avoid postblit hopefully) but when it's an Rvalue I need to make a second function either duplicate except signature or forwarding the value.

> The one huge problem I've had with lack of rvalue references is with arithmetic operators:
>
> struct M
> {
>   M opAdd(const ref M other) const {...}
> }
>
> M m;
>
> auto m2 = (m + m) + m; // ok!
> auto m3 = m + (m + m); // error!
>
> This is crap.

 If 'auto ref' gets accepted for non-template functions, it goes away. With M as you show, returning ref doesn't work so that example I was going to suggest doesn't work.
March 02, 2013
On Fri, 01 Mar 2013 19:49:54 -0500, Era Scarecrow <rtcvb32@yahoo.com> wrote:

> On Friday, 1 March 2013 at 23:46:36 UTC, Steven Schveighoffer wrote:

>> This is the major problem that Andrei had with it (at least as I understand his past statements) -- it conflates const with rvalue references.  Sometimes, you want a const ref that does NOT bind to an rvalue.
>
>   If I have a medium sized struct that I only intend to read/reference from I see no reason not to use 'const ref' for simplicity and speed (and avoid postblit hopefully) but when it's an Rvalue I need to make a second function either duplicate except signature or forwarding the value.

The point is simple:

foo(ref M m)
foo(M m)

An lvalue, yes, we want that to bind to ref.

But an rvalue?  I want that to bind to foo(M m), otherwise I would not have added that method.  The by-value version is *more efficient* than the by-ref version with rvalues, even for large structs.

But what if I ALSO want to say that foo doesn't change m?  Well, that's easy!  I just do:

foo(const ref M m)

But this makes rvalues bind to that version too!  This is the problem.  I want it to be ref, to avoid copies of a larg struct, and I want it to be const, for contract purposes, I DIDN'T want it to accept rvalues.

The point is, there is a legitimate reason to mark a parameter const ref BESIDES wanting to have it bind to rvalues.

>> The one huge problem I've had with lack of rvalue references is with arithmetic operators:
>>
>> struct M
>> {
>>   M opAdd(const ref M other) const {...}
>> }
>>
>> M m;
>>
>> auto m2 = (m + m) + m; // ok!
>> auto m3 = m + (m + m); // error!
>>
>> This is crap.
>
>   If 'auto ref' gets accepted for non-template functions, it goes away. With M as you show, returning ref doesn't work so that example I was going to suggest doesn't work.

In my code base, I have actual comments that explain why I have ordered certain operations the way I did!

But we have more problems than just rvalue references.  The compiler doesn't "see through" structs to know whether something is an lvalue or an rvalue.

Consider writing your own pointer type:

struct T
{
    int *x;
    void opUnary(string op)() if (op == "++") {++(*x);}
}

int x;

T foo()
{
    return T(&x);
}

void main()
{
    auto t = foo;
    t++; // ok
    foo++; // Error: foo() is not an lvalue
}

That should not be an error, or I should at least be able to tell the compiler "this is NOT an rvalue, even if it seems like one".

-Steve
« First   ‹ Prev
1 2 3