Unlike C++, const ref _must_ be an lvalue just like ref. If you use auto ref
instead of const ref, the compiler is supposed to choose between ref and non-
ref based on which it thinks would be more efficient, but it currently only
works with templated types.
You can duplicate the function and have a version whose parameter is const ref
and one which is not, but be careful if you try and make the non-const ref
version call the const ref version (to avoid duplicating the function's body)
and make sure that you don't get infinite recursion. It usually works, but I've
run into cases before where I ended up with infinite recursion, so make sure
that you have unit tests which check.
Regardless, if you're dealing with a primitive type like double, don't bother
with const ref. It's not going to be more efficient. It's with large structs
that you can see a difference.
- Jonathan M Davis

Unlike C++, const ref _must_ be an lvalue just like ref. If you
use auto ref
instead of const ref, the compiler is supposed to choose
between ref and non-
ref based on which it thinks would be more efficient, but it
currently only
works with templated types.
You can duplicate the function and have a version whose
parameter is const ref
and one which is not, but be careful if you try and make the
non-const ref
version call the const ref version (to avoid duplicating the
function's body)
and make sure that you don't get infinite recursion. It usually
works, but I've
run into cases before where I ended up with infinite recursion,
so make sure
that you have unit tests which check.
Regardless, if you're dealing with a primitive type like
double, don't bother
with const ref. It's not going to be more efficient. It's with
large structs
that you can see a difference.
- Jonathan M Davis

Unlike C++, const ref _must_ be an lvalue just like ref. If you
use auto ref
instead of const ref, the compiler is supposed to choose
between ref and non-
ref based on which it thinks would be more efficient, but it
currently only
works with templated types.
You can duplicate the function and have a version whose
parameter is const ref
and one which is not, but be careful if you try and make the
non-const ref
version call the const ref version (to avoid duplicating the
function's body)
and make sure that you don't get infinite recursion. It usually
works, but I've
run into cases before where I ended up with infinite recursion,
so make sure
that you have unit tests which check.
Regardless, if you're dealing with a primitive type like
double, don't bother
with const ref. It's not going to be more efficient. It's with
large structs
that you can see a difference.
- Jonathan M Davis

Unlike C++, const ref _must_ be an lvalue just like ref. If you
use auto ref
instead of const ref, the compiler is supposed to choose
between ref and non-
ref based on which it thinks would be more efficient, but it
currently only
works with templated types.
You can duplicate the function and have a version whose
parameter is const ref
and one which is not, but be careful if you try and make the
non-const ref
version call the const ref version (to avoid duplicating the
function's body)
and make sure that you don't get infinite recursion. It usually
works, but I've
run into cases before where I ended up with infinite recursion,
so make sure
that you have unit tests which check.
Regardless, if you're dealing with a primitive type like
double, don't bother
with const ref. It's not going to be more efficient. It's with
large structs
that you can see a difference.
- Jonathan M Davis

You misunderstood what I meant by auto ref. I meant that you use auto ref
instead of const ref. e.g.
void func(auto ref T param) {}
At present, func would have to be templated to use auto ref
void func(T)(auto ref T param) {}
but eventually, auto ref shouldn't need the function to be templated.
Returning auto ref is completely different. It says that the return type is
inferred (just like with returning auto by itself) except that it's a
reference to the type which is inferred. And that requires that the value
being returned by an lvalue which is _not_ a local variable (because in all
cases except for auto ref on a parameter, ref must refer to an lvalue, and if
you return a ref to a local variable, you're going to get undefined behavior,
because you're returned a reference to a variable which no longer exists). You
can return ref from a function if you're returning the object's this pointer,
but that's generally all.
If you're really paranoid about copying, then you're going to need to either
us auto ref on your parameters (which currently means that the functions must
be templated), or you need to use const ref (which then requires that you use
lvalues with those functions). Something like
((abc1+abc2*20.0)/0.25) + (abc1/abc2)*(abc2-1)....
makes _no_ sense if you're trying to avoid copying. By definition, those
operations must create temporaries, because they're returning new values.
That's exactly what happens with the built-in types. +, *, /, etc. all create
temporaries. You need to use +=, *=, /=, etc if you don't want to create
temporaries. The same would be true in C++.
But honestly, in your example, there is _no_ reason to worry about it. You
have a struct that holds one double, and that's it. So, it's going to be about
as efficient as if all of those operations were operating on a double rather
than a struct. If your struct had ten different variables, then maybe you'd
need to be worried about copying, but not with just one.
Regardless, if you want to avoid creating temporaries, you can't use operators
which return new values. You need to use the opOpAssign operators, which means
that your code is going to look very different, because you won't be able to
create expressions like the one above.
By using auto ref and/or const ref, you can create functions which don't make
copies when passed a variable, but as long as you're using functions/operators
which return new values, you're still going to be creating temporaries.
- Jonathan M Davis

Unlike C++, const ref _must_ be an lvalue just like ref. If
you
use auto ref
instead of const ref, the compiler is supposed to choose
between ref and non-
ref based on which it thinks would be more efficient, but it
currently only
works with templated types.
You can duplicate the function and have a version whose
parameter is const ref
and one which is not, but be careful if you try and make the
non-const ref
version call the const ref version (to avoid duplicating the
function's body)
and make sure that you don't get infinite recursion. It
usually
works, but I've
run into cases before where I ended up with infinite
recursion,
so make sure
that you have unit tests which check.
Regardless, if you're dealing with a primitive type like
double, don't bother
with const ref. It's not going to be more efficient. It's
with
large structs
that you can see a difference.
- Jonathan M Davis

You misunderstood what I meant by auto ref. I meant that you
use auto ref
instead of const ref. e.g.
void func(auto ref T param) {}
At present, func would have to be templated to use auto ref
void func(T)(auto ref T param) {}
but eventually, auto ref shouldn't need the function to be
templated.
Returning auto ref is completely different. It says that the
return type is
inferred (just like with returning auto by itself) except that
it's a
reference to the type which is inferred. And that requires that
the value
being returned by an lvalue which is _not_ a local variable
(because in all
cases except for auto ref on a parameter, ref must refer to an
lvalue, and if
you return a ref to a local variable, you're going to get
undefined behavior,
because you're returned a reference to a variable which no
longer exists). You
can return ref from a function if you're returning the object's
this pointer,
but that's generally all.
If you're really paranoid about copying, then you're going to
need to either
us auto ref on your parameters (which currently means that the
functions must
be templated), or you need to use const ref (which then
requires that you use
lvalues with those functions). Something like
((abc1+abc2*20.0)/0.25) + (abc1/abc2)*(abc2-1)....
makes _no_ sense if you're trying to avoid copying. By
definition, those
operations must create temporaries, because they're returning
new values.
That's exactly what happens with the built-in types. +, *, /,
etc. all create
temporaries. You need to use +=, *=, /=, etc if you don't want
to create
temporaries. The same would be true in C++.
But honestly, in your example, there is _no_ reason to worry
about it. You
have a struct that holds one double, and that's it. So, it's
going to be about
as efficient as if all of those operations were operating on a
double rather
than a struct. If your struct had ten different variables, then
maybe you'd
need to be worried about copying, but not with just one.
Regardless, if you want to avoid creating temporaries, you
can't use operators
which return new values. You need to use the opOpAssign
operators, which means
that your code is going to look very different, because you
won't be able to
create expressions like the one above.
By using auto ref and/or const ref, you can create functions
which don't make
copies when passed a variable, but as long as you're using
functions/operators
which return new values, you're still going to be creating
temporaries.
- Jonathan M Davis

About temporaries in operators +-/*.. you're right, it is not a
secret.
references in other functions (not in operators) too.
I can't understand one thing:
ABC abc; // ABC is a struct that consists of some 4x4 matrix for
example
ABC abc2 = ABC(20);
ABC abc3 = ABC(..);
.........
abc+=abc2+abc3;
operation "+" return the copy of it result to the internal
temporary stack variable $temp (it creates automatically and it
disable to use and invisible to see), but $temp is a variable and
it must be an lvalue always. Expression "ref const.." in function
parameter is an lvalue in D. Why $temp variable can't be passes
as "ref const..." to the += operator or any other orepators)?
I think, that the $temp in D is a rvalue, but expression "ref
const.." is an lvalue.. some paradox, and out of understanding..
if "ref const.." in operators parameters would be an rvalue, the
execution of the next cycle be a 2x faster:
// "ref const.." is an lvalue. ***********************
ABC sum;
.....
for (int i = 0; i<100000000; i++)
{
sum = (abc1+abc2*20.0)+(abc1*abc2+abc1*20.0);
//abc2.opMul(20.0) creates $temp1;
//abc1.opAdd(copy of $temp1) creates $temp2
//abc1.opMul(copy of abc2) creates $temp3
//abc1.opMul(20.0) creates $temp4
//$temp3.opAdd(copy of $temp4) creates $temp5
//$temp2.opAdd(copy of $temp5) creates $temp6
//sum.opAssign(copy of $temp6)
}
RESULT: execution time 17.8 seconds
5 unnecessary copy operations:
1. copy of $temp1
2. copy of $abc2
3. copy of $temp4
4. copy of $temp5
5. copy of $temp6
// "ref const.." is an rvalue: **************************
ABC sum;
.....
for (int i = 0; i<100000000; i++)
{
sum = (abc1+abc2*20.0)+(abc1*abc2+abc1*20.0);
//abc2.opMul(20.0) creates $temp1;
//abc1.opAdd(ref to $temp1) creates $temp2
//abc1.opMul(ref to abc2) creates $temp3
//abc1.opMul(20.0) creates $temp4
//$temp3.opAdd(ref to $temp4) creates $temp5
//$temp2.opAdd(ref to $temp5) creates $temp6
//sum.opAssign(ref to $temp6)
}
RESULT: execution time 9.2 seconds
no unnecessary copy operations.
when I draw to the screen 50000 graphical objects by OpenGL, it
uses a matrices operation via D-language structures (uses all
math operators and makes some matrices operations for every
graphical object on the scene for each time), the unnecessary
copyes of it(matrices) is very bad for engine performance.
Thank you!

About temporaries in operators +-/*.. you're right, it is not a
secret.
references in other functions (not in operators) too.
I can't understand one thing:
ABC abc; // ABC is a struct that consists of some 4x4 matrix for
example
ABC abc2 = ABC(20);
ABC abc3 = ABC(..);
.........
abc+=abc2+abc3;
operation "+" return the copy of it result to the internal
temporary stack variable $temp (it creates automatically and it
disable to use and invisible to see), but

$temp is a variable and
it must be an lvalue always.

No. It's not. It's a temporary, and temporaries are almost always rvalues. The
sole (and very bizarre) exception is struct literals (e.g. ABC(20) is
currently considered an lvalue). It results in the bizarre situation that a
function which takes a const ref will work with ABC(20) but won't work with a
function that returns ABC(20). It's a point of debate and may be changed, in
which case ABC(20) would not be an lvalue anymore. But regardless, the result
of operations such as + are _always_ rvalues.
Normally, the only items that are lvalues are variables and return values
which are ref. The result of a function or operator such as + is most
definitely _not_ an lvalue, since you can't return their results by ref. It's a
classic example on an rvalue.
If you want to avoid making copies, then you're going to need to make your
parameters auto ref (and hope that the compiler decides that making a copy is
more expensive - I don't know how it decides that) or make them const ref and
use lvalues - and using lvalues generally means creating explicit variables
and _not_ using temporaries. So, your long expression with + and * would have
to be changed to use += and *=.

when I draw to the screen 50000 graphical objects by OpenGL, it
uses a matrices operation via D-language structures (uses all
math operators and makes some matrices operations for every
graphical object on the scene for each time), the unnecessary
copyes of it(matrices) is very bad for engine performance.

Sure, if you have large structs, making a lot of copies of them can be
expensive. But to avoid that, you're going to have to avoid coding in a way
which creates temporaries, and expressions like
(abc1+abc2*20.0)+(abc1*abc2+abc1*20.0)
will _always_ create temporaries. The classic, arithmetic operators are
classic examples of functions which create temporaries (all of which are
rvalues). So, you can't code that way and expect to avoid temporaries.
- Jonathan M Davis

No. It's not. It's a temporary, and temporaries are almost always
rvalues. The
sole (and very bizarre) exception is struct literals (e.g. ABC(20) is
currently considered an lvalue). It results in the bizarre situation
that a
function which takes a const ref will work with ABC(20) but won't work
with a
function that returns ABC(20). It's a point of debate and may be
changed, in
which case ABC(20) would not be an lvalue anymore. But regardless, the
result
of operations such as + are _always_ rvalues.

Normally, the only items that are lvalues are variables and return values
which are ref. The result of a function or operator such as + is most
definitely _not_ an lvalue, since you can't return their results by ref.
It's a
classic example on an rvalue.
If you want to avoid making copies, then you're going to need to make
your
parameters auto ref (and hope that the compiler decides that making a
copy is
more expensive - I don't know how it decides that) or make them const
ref and
use lvalues - and using lvalues generally means creating explicit
variables
and _not_ using temporaries. So, your long expression with + and * would
have
to be changed to use += and *=.

when I draw to the screen 50000 graphical objects by OpenGL, it
uses a matrices operation via D-language structures (uses all
math operators and makes some matrices operations for every
graphical object on the scene for each time), the unnecessary
copyes of it(matrices) is very bad for engine performance.

Sure, if you have large structs, making a lot of copies of them can be
expensive. But to avoid that, you're going to have to avoid coding in a
way
which creates temporaries, and expressions like
(abc1+abc2*20.0)+(abc1*abc2+abc1*20.0)
will _always_ create temporaries. The classic, arithmetic operators are
classic examples of functions which create temporaries (all of which are
rvalues). So, you can't code that way and expect to avoid temporaries.
- Jonathan M Davis

On Monday, April 02, 2012 05:46:24 L-MAN wrote:
Sure, if you have large structs, making a lot of copies of them can be
expensive. But to avoid that, you're going to have to avoid coding in a way
which creates temporaries, and expressions like
(abc1+abc2*20.0)+(abc1*abc2+abc1*20.0)
will _always_ create temporaries. The classic, arithmetic operators are
classic examples of functions which create temporaries (all of which are
rvalues). So, you can't code that way and expect to avoid temporaries.

I should point out that with optimizations turned on, in some cases the
compiler can optimize out the copying of temporaries, though not generally the
temporaries themselves. For instance
auto a = abc1 + abc2 * 20.0
results in a temporary for the result of * and a temporary for the result of
+. But the temporary for the result of + will be optimized out (probably even
without turning on optimizations), because you're using that result to
initialize a variable. And while the result of * will result in a temporary,
the copy that gets made when it's passed to opBinary!"+" should be optimized
out when compiled with optimizations turned on. So, the optimizer should be
able to reduce the problem a fair bit if you compile with -O. -inline may be
able to reduce it even further. So, with optimizations turned on, you may not
get anywhere near as many copies as you expected.
And using auto ref for the parameters (which is easy with the overloaded
arithmetic operators, since they're already templated) should also help reduce
the number of copies made, but it _is_ up to the compiler whether it makes a
copy or not with auto ref, so that depends on the compiler.
So, you should be able to reduce the number of copies simply by compiling with
-O and -inline, but the temporaries themselves will exist regardless.
- Jonathan M Davis

On Monday, April 02, 2012 05:46:24 L-MAN wrote:
Sure, if you have large structs, making a lot of copies of
them can be
expensive. But to avoid that, you're going to have to avoid
coding in a way
which creates temporaries, and expressions like
(abc1+abc2*20.0)+(abc1*abc2+abc1*20.0)
will _always_ create temporaries. The classic, arithmetic
operators are
classic examples of functions which create temporaries (all of
which are
rvalues). So, you can't code that way and expect to avoid
temporaries.

I should point out that with optimizations turned on, in some
cases the
compiler can optimize out the copying of temporaries, though
not generally the
temporaries themselves. For instance
auto a = abc1 + abc2 * 20.0
results in a temporary for the result of * and a temporary for
the result of
+. But the temporary for the result of + will be optimized out
(probably even
without turning on optimizations), because you're using that
result to
initialize a variable. And while the result of * will result in
a temporary,
the copy that gets made when it's passed to opBinary!"+" should
be optimized
out when compiled with optimizations turned on. So, the
optimizer should be
able to reduce the problem a fair bit if you compile with -O.
-inline may be
able to reduce it even further. So, with optimizations turned
on, you may not
get anywhere near as many copies as you expected.
And using auto ref for the parameters (which is easy with the
overloaded
arithmetic operators, since they're already templated) should
also help reduce
the number of copies made, but it _is_ up to the compiler
whether it makes a
copy or not with auto ref, so that depends on the compiler.
So, you should be able to reduce the number of copies simply by
compiling with
-O and -inline, but the temporaries themselves will exist
regardless.
- Jonathan M Davis