Ultimate Efficiency

J

JKop

Your program begins at:

int main(void);


Now, in main, you want to call a function. This particular function you want
to call defines a local object of type Taste, and then returns this local
object by value.

In main, having called this function, what you want to do is bind a
reference to the object returned from Taste and then continue on and use it
just as if it were defined in main as a local object.

The first restriction here is that the Standard declares that you can bind
the object returned from a function only to a *const* reference.

Altogether, you want just 1 object to be created. No temporaries. No copies.


But... in actual fact you're going to have 3 objects:

1: The local variable defined in the function

2: The temporary returned

3: You'll have to create another object in main and copy the temporary so as
to alleviate the constness.


I wrote the following code a few mins ago and it compiles and runs grand.
I've tried my best to make the code as non-tedious as possible to read, so
please just give it a glance over:


#include <iostream>

class Taste
{
public:

static unsigned char amount_ever;

static unsigned char amount_currently;


int mouth;

unsigned char age;


void Swallow(void)
{
mouth += 2;
}

Taste(void) : mouth(7), age(14)
{
++amount_ever;
++amount_currently;
}

Taste(Taste& original)
{
mouth = original.mouth;
age = original.age;

++amount_ever;
++amount_currently;
}

~Taste(void)
{
--amount_currently;
}
};


unsigned char Taste::amount_ever = 0;

unsigned char Taste::amount_currently = 0;


Taste Powder(void)
{
Taste cream;

cream.age = 4;

cream.Swallow();

return cream;
}


int main(void)
{
const Taste& yacht = Powder();

Taste& boat = const_cast<Taste&>(yacht);


//Now we have what we wanted, a local variable in main.

boat.age = 12;

boat.Swallow();



std::cout << "Amount ever: " << (int)Taste::amount_ever << std::endl
<< "Amount currently: " << (int)Taste::amount_currently
<< std::endl;

std::system("PAUSE");

}



Now first thing's first:

I'm going to presume that casting away that constness and editing the object
is undefined behaviour.
Can anyone come up with any argument as to why the hell you've to bind to a
*const* reference in the first place?! I'll wait for an answer to this
question before I decide for myself if casting away the constness is
"moral".


Moving on:

With the above code, by casting away the constness, I was aiming for
amount_ever == 2. To my delight, it came up 1!!
Obviously, *my* compiler has not created a temporary, it has returned the
actual local object defined in the function. Which leads me to...
Why the hell is this "optimization" compiler behaviour, as opposed to
"run-of-the-mill" compiler behaviour?!! Can anyone supply me with any
arguments as to why a function should copy the local variable and then
return the copy, as opposed to just returning the local variable itself?!


Here's my thoughts:

My objective is very clear. In my mind, I know and trully believe that I
should be able to do what I want to do with just the 1 object, as opposed to
3! Without regard to the Standard, I believe that C++ as an excellent
programming language should be able to achieve this, that it should be "run-
of-the-mill" compiler behaviour, as opposed to "optimization" compiler
behaviour.

-JKop
 
I

Ioannis Vranos

JKop said:
Your program begins at:

int main(void);


Now, in main, you want to call a function. This particular function you want
to call defines a local object of type Taste, and then returns this local
object by value.

In main, having called this function, what you want to do is bind a
reference to the object returned from Taste and then continue on and use it
just as if it were defined in main as a local object.

The first restriction here is that the Standard declares that you can bind
the object returned from a function only to a *const* reference.

Altogether, you want just 1 object to be created. No temporaries. No copies.


But... in actual fact you're going to have 3 objects:

1: The local variable defined in the function

2: The temporary returned

3: You'll have to create another object in main and copy the temporary so as
to alleviate the constness.



Or you can pass your object from main by reference. Did you forget that?




Now first thing's first:

I'm going to presume that casting away that constness and editing the object
is undefined behaviour.
Can anyone come up with any argument as to why the hell you've to bind to a
*const* reference in the first place?! I'll wait for an answer to this
question before I decide for myself if casting away the constness is
"moral".


By casting away the constness, you use a non-const reference to a
temporary which is going to be destroyed at the end of yacht scope. Also
this kind of hackery means that the programmer doesn't know exactly what
he wants to do.



Moving on:

With the above code, by casting away the constness, I was aiming for
amount_ever == 2. To my delight, it came up 1!!
Obviously, *my* compiler has not created a temporary, it has returned the
actual local object defined in the function. Which leads me to...
Why the hell is this "optimization" compiler behaviour, as opposed to
"run-of-the-mill" compiler behaviour?!! Can anyone supply me with any
arguments as to why a function should copy the local variable and then
return the copy, as opposed to just returning the local variable itself?!


Why did you expect it to be 2? The temporary should have the value 1 for
both amount_ever ans amount_currently.


The local variable is destroyed at the end of its scope. Anything else
would be against C++ rules.


Here's my thoughts:

My objective is very clear. In my mind, I know and trully believe that I
should be able to do what I want to do with just the 1 object, as opposed to
3!


My suggestion is to have a slow, thorough read of an up to date ISO C++
book.


Without regard to the Standard, I believe that C++ as an excellent
programming language should be able to achieve this, that it should be "run-
of-the-mill" compiler behaviour, as opposed to "optimization" compiler
behaviour.

:) If you have space/run-time efficiency concerns you should pass by
reference (using pointers or references). It has nothing to do with C++
but with the proper use of it.






Regards,

Ioannis Vranos
 
J

JKop

Ioannis Vranos posted:
Or you can pass your object from main by reference. Did you forget
that?


The function's already written. Look at it from that point of view.

I think it's cleaner that the function itself should have the obligation of
declaring the variable and giving it back. You have the choice to just let
the returned object die, or prolong its life by binding it to a reference.

By casting away the constness, you use a non-const reference to a
temporary which is going to be destroyed at the end of yacht scope.


Exactly my intention. boat and yacht have the same scope in anyway, so
there's no problem there.

Why did you expect it to be 2? The temporary should have the value 1
for both amount_ever ans amount_currently.


The temporary should?

amount_ever and amount_currently are static

Tasete::amount_ever

I was expecting it to be 2 because my Powder function "should" have made a
copy of its local object and then returned the copy, aka a temporary. This
would bring the count to 2.

The local variable is destroyed at the end of its scope. Anything else
would be against C++ rules.


First you said that amount_ever should be 1.
Now you're saying that it would be against C++ rules to return the local
object, indicating that the local variable should be copied to a temporary
and the temporary be returned. This brings the count to 2.

A) No temporary. Return local variable. amount_ever = 1

B) Create temporay. Return temporary. amount_ever = 2


-JKop
 
A

Andre Kostur

JKop said:
Ioannis Vranos posted:



The function's already written. Look at it from that point of view.

I think it's cleaner that the function itself should have the
obligation of declaring the variable and giving it back. You have the
choice to just let the returned object die, or prolong its life by
binding it to a reference.

Or why not:

Taste yacht(Powder());

And let the Return Value Optimization deal with the extra copies of Taste
which can be eliminated?
 
I

Ioannis Vranos

JKop said:
The function's already written. Look at it from that point of view.



Then the library maker must have a slow, thorough read of an up to date
ISO C++ book, as I have been doing myself too.


Another alternative could be the function to create the object on the
free store and return a pointer to it.

In any case, what you have done is not used for objects having large
space/time costs.


I think it's cleaner that the function itself should have the obligation of
declaring the variable and giving it back. You have the choice to just let
the returned object die, or prolong its life by binding it to a reference.


All are a matter of logic. If you are expecting that the function has
the obligation to create that type of object, then you should make it
create it on the free store and return it back to the caller.


The temporary should?

amount_ever and amount_currently are static

Tasete::amount_ever

I was expecting it to be 2 because my Powder function "should" have made a
copy of its local object and then returned the copy, aka a temporary. This
would bring the count to 2.



Ok I got a head ache, let's use this simpler code for reference:


#include <iostream>
#include <cstdlib>

class test
{
public:
static int counter;

test() { ++counter; }
};

int test::counter=0;


test somefunc()
{
test a;

return a;
}



int main()
{
using namespace std;

somefunc();

cout<<test::counter<<endl;

system("pause");
}


G++ produces:

C:\c>temp
1
Press any key to continue . . .


MSVC++ produces:

2
Press any key to continue . . .



Obviously G++ performs an optimisation and uses the same object instead
of creating a temporary one. Strictly speaking it is a G++ bug, however
probably its makers considered that this optimisation has not real-life
implications.

Strictly ISO C++ speaking, you can report it as a defect.


First you said that amount_ever should be 1.
Now you're saying that it would be against C++ rules to return the local
object, indicating that the local variable should be copied to a temporary
and the temporary be returned. This brings the count to 2.


Yes, it should be 2. At the first time I was confused with your
redundant counter usage.






Regards,

Ioannis Vranos
 
B

Branimir Maksimovic

Ioannis Vranos said:
Ok I got a head ache, let's use this simpler code for reference:


#include <iostream>
#include <cstdlib>

class test
{
public:
static int counter;

test() { ++counter; }

you forgot
test(const test&)
{
++counter;
}
};

int test::counter=0;


test somefunc()
{
test a;

return a;
}



int main()
{
using namespace std;

somefunc();

cout<<test::counter<<endl;

system("pause");
}


G++ produces:

C:\c>temp
1
Press any key to continue . . .


MSVC++ produces:

2
Press any key to continue . . .
I presume that you have copy constructor
in compiled version.
Obviously G++ performs an optimisation and uses the same object instead
of creating a temporary one. Strictly speaking it is a G++ bug, however
probably its makers considered that this optimisation has not real-life
implications.

Strictly ISO C++ speaking, you can report it as a defect.

I don't think so. Check out 12.18.15:

"
Whenever a temporary class object is copied using a copy constructor, and
this object and the copy have the same cvunqualified
type, an implementation is permitted to treat the original and the copy as
two different ways of referring to the same object and not perform a copy at
all, even if the class copy constructor or destructor have
side effects. For a function with a class return type, if the expression in
the return statement is the name of a local object, and the cvunqualified
type of the local object is the same as the function
return type, an implementation is permitted to omit creating the temporary
object to hold the function return value, even if the class
copy constructor or destructor has side effects. In these cases, the object
is destroyed at the later of times when the original and the copy would have
been destroyed without the optimization.

111) [Example:

class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
Thing operator=(const Thing&);
void fun();
};

Thing f() {
Thing t;
return t;
}

Thing t2 = f();

Here t does not need to be copied when returning from f. The return value of
f may be constructed directly into the object t2. ]

"

Greetings, Bane.
 
J

JKop

Branimir Maksimovic posted:
I don't think so. Check out 12.18.15:

"
Whenever a temporary class object is copied using a copy constructor,
and this object and the copy have the same cvunqualified
type, an implementation is permitted to treat the original and the copy
as two different ways of referring to the same object and not perform a
copy at all, even if the class copy constructor or destructor have
side effects. For a function with a class return type, if the
expression in
the return statement is the name of a local object, and the
cvunqualified type of the local object is the same as the function
return type, an implementation is permitted to omit creating the
temporary object to hold the function return value, even if the class
copy constructor or destructor has side effects. In these cases, the
object
is destroyed at the later of times when the original and the copy would
have been destroyed without the optimization.


This pleases me.

But... that doesn't address the issue of a temporary being made in the first
place when one is not necesary! For example:

int monkey(void)
{
return 22;
}

In the above, a temporary definitely is necessary.

int monkey(void)
{
int cow = 22;

return cow;
}

In the above, a temporary is unncecessary. It can simply just return cow.
But does it...? And what does the Standard have to say about it?

111) [Example:

class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
Thing operator=(const Thing&);
void fun();
};

Thing f() {
Thing t;
return t;
}

Thing t2 = f();

Here t does not need to be copied when returning from f. The return
value of f may be constructed directly into the object t2. ]

Just so you know, the operator= is never called in your code.


-JKop
 
B

Branimir Maksimovic

JKop said:
But... that doesn't address the issue of a temporary being made in the first
place when one is not necesary! For example:

int monkey(void)
{
return 22;
}

In the above, a temporary definitely is necessary.

What temporary? As I understand when talking about
temporaries we are thinking about instances of
structs/classes with constructors.
Above can be compiled with just two assembly
instructions:
move 22 to register and return from function.
int monkey(void)
{
int cow = 22;

return cow;
}

In the above, a temporary is unncecessary.
There is no temporary in this case either.
It can be compiled with just two assembly
instructions as previous example.
It can simply just return cow.
But does it...?
And what does the Standard have to say about it?

"
[basic.stc.auto] 3.7.2 Automatic storage duration

1 Local objects explicitly declared auto or register or not explicitly
declared static or extern have automatic storage duration. The storage for
these objects lasts until the block in which they are created exits.

2 [Note: these objects are initialized and destroyed as described in 6.7. ]

3 If a named automatic object has initialization or a destructor with side
effects, it shall not be destroyed before the end of its block, nor shall it
be eliminated as an optimization even if it appears to be unused, except
that a class object or its copy may be eliminated as specified in 12.8.

"

It is clear that return value optimization only refers to
classes/structs with constructors/destructors
111) [Example:

class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
Thing operator=(const Thing&);
void fun();
};

Thing f() {
Thing t;
return t;
}

Thing t2 = f();

Here t does not need to be copied when returning from f. The return
value of f may be constructed directly into the object t2. ]

Just so you know, the operator= is never called in your code.

This is just one of two forms of initialization.

"

The initialization that occurs in argument passing, function return,
throwing an exception (15.1), handling an exception (15.3), and
braceenclosed initializer lists (8.5.1) is called copyinitialization
and is equivalent to the form

T x = a;

The initialization that occurs in new expressions (5.3.4), static_cast
expressions (5.2.9), functional notation type conversions (5.2.3), and base
and member initializers (12.6.2) is called directinitialization
and is equivalent to the form

T x(a);

"


Greetings, Bane.
 
B

Branimir Maksimovic

JKop said:
Branimir Maksimovic posted:


int monkey(void)
{
int cow = 22;

return cow;
}

In the above, a temporary is unncecessary. It can simply just return cow.

Ah, think I know what you mean by temporary.

const int& tmpref = monkey();

in this case termorary variable must be created in order to
initialize reference with it.

int notemp = monkey();

in this case there is no temporary variable created.

Greetings, Bane.
 
I

Ioannis Vranos

JKop said:
//It's the copies we're woried about!



Actually I copied and pasted the wrong code but with the right results.
The correct one:


#include <iostream>
#include <cstdlib>

class test
{
public:
static int counter;

test() { ++counter; }
test(const test &x) { ++counter; }
};

int test::counter=0;


test somefunc()
{
test a;

return a;
}



int main()
{
using namespace std;

const test&r=somefunc();

cout<<test::counter<<endl;

system("pause");
}



It produces 2 with MSVC++ and 1 with G++.






Regards,

Ioannis Vranos
 
I

Ioannis Vranos

Branimir Maksimovic wrote:

I don't think so. Check out 12.18.15:


12.8.15. It took few time to track it down.


"
Whenever a temporary class object is copied using a copy constructor, and
this object and the copy have the same cvunqualified
type, an implementation is permitted to treat the original and the copy as
two different ways of referring to the same object and not perform a copy at
all, even if the class copy constructor or destructor have
side effects. For a function with a class return type, if the expression in
the return statement is the name of a local object, and the cvunqualified
type of the local object is the same as the function
return type, an implementation is permitted to omit creating the temporary
object to hold the function return value, even if the class
copy constructor or destructor has side effects. In these cases, the object
is destroyed at the later of times when the original and the copy would have
been destroyed without the optimization.



Well, there is always something new to learn. Thanks for the
information. :)






Regards,

Ioannis Vranos
 
I

Ioannis Vranos

Branimir said:
This is just one of two forms of initialization.

"

The initialization that occurs in argument passing, function return,
throwing an exception (15.1), handling an exception (15.3), and
braceenclosed initializer lists (8.5.1) is called copyinitialization
and is equivalent to the form

T x = a;

The initialization that occurs in new expressions (5.3.4), static_cast
expressions (5.2.9), functional notation type conversions (5.2.3), and base
and member initializers (12.6.2) is called directinitialization
and is equivalent to the form

T x(a);

"



Doesn't the above mean that the temporary returned from a function call
is created using operator=() instead of copy constructor?!


However as far as I know(?) the copy constructor is used in this case.






Regards,

Ioannis Vranos
 
K

Karl Heinz Buchegger

Ioannis said:
Doesn't the above mean that the temporary returned from a function call
is created using operator=() instead of copy constructor?!
No.


However as far as I know(?) the copy constructor is used in this case.

Right. (Object creation is always handled through some constructor. operator=
would need an already existing object, which is not there when the object
gets born)

In

T x = a

no operator= is involved. It is equivalent to

T x( T(a) );

First create a temporary from 'a' by taking a constructor which
takes an 'a', then use that temporary in a copy constructor to
create the final object.
 
B

Branimir Maksimovic

Ioannis Vranos said:
Doesn't the above mean that the temporary returned from a function call
is created using operator=() instead of copy constructor?!

No. It is initialization of object, not assignment.

T x(a); said:
However as far as I know(?) the copy constructor is used in this case.

Yes, but not always.

T func()
{
T tmp;
//...
return tmp;
}

If tmp is optimised out (RVO),
return value temporary is constructed with default constructor
so copy constructor is never called.

Greetings, Bane.
 
J

JKop

Branimir Maksimovic posted:
Ah, think I know what you mean by temporary.

const int& tmpref = monkey();

in this case termorary variable must be created in order to
initialize reference with it.

int notemp = monkey();

in this case there is no temporary variable created.

Nope, it's not the calling function that's creating the temporary, but the
*called* function.

-JKop
 
B

Branimir Maksimovic

JKop said:
Branimir Maksimovic posted:


Nope, it's not the calling function that's creating the temporary, but the
*called* function.

Just check out assembly code created from compiler.
How and where do you think temporary variable is created in
called function?

Greetings, Bane.
 
I

Ioannis Vranos

JKop said:
//It's the copies we're woried about!



Today there was a much with my Usenet server and I do not know how much
of my messages shown up, but to make sure that this one is shown, I am
reposting it:



Actually I copied and pasted the wrong code but with the right results.
The correct one:


#include <iostream>
#include <cstdlib>

class test
{
public:
static int counter;

test() { ++counter; }
test(const test &x) { ++counter; }
};

int test::counter=0;


test somefunc()
{
test a;

return a;
}



int main()
{
using namespace std;

const test&r=somefunc();

cout<<test::counter<<endl;

system("pause");
}



It produces 2 with MSVC++ and 1 with G++.






Regards,

Ioannis Vranos
 
J

JKop

Branimir Maksimovic posted:
Just check out assembly code created from compiler.
How and where do you think temporary variable is created in
called function?


I don't know assembly!


-JKop
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top