Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Ultimate Efficiency

Reply
Thread Tools

Ultimate Efficiency

 
 
JKop
Guest
Posts: n/a
 
      06-21-2004

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
 
Reply With Quote
 
 
 
 
Ioannis Vranos
Guest
Posts: n/a
 
      06-21-2004
JKop wrote:

> 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
 
Reply With Quote
 
 
 
 
JKop
Guest
Posts: n/a
 
      06-21-2004
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
 
Reply With Quote
 
Andre Kostur
Guest
Posts: n/a
 
      06-21-2004
JKop <(E-Mail Removed)> wrote in news:w8HBc.2807$(E-Mail Removed):

> 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.


Or why not:

Taste yacht(Powder());

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

 
Reply With Quote
 
Ioannis Vranos
Guest
Posts: n/a
 
      06-21-2004
JKop wrote:

> 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
 
Reply With Quote
 
JKop
Guest
Posts: n/a
 
      06-22-2004
> class test
> {
> public:
> static int counter;
>
> test() { ++counter; }


test(test& original)
{
++counter;
}

//It's the copies we're woried about!

> };
>
> int test::counter=0;

 
Reply With Quote
 
Branimir Maksimovic
Guest
Posts: n/a
 
      06-22-2004

"Ioannis Vranos" <(E-Mail Removed)> wrote in message
news:cb7hfo$dmo$(E-Mail Removed)...
> 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.









 
Reply With Quote
 
JKop
Guest
Posts: n/a
 
      06-22-2004
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
 
Reply With Quote
 
Branimir Maksimovic
Guest
Posts: n/a
 
      06-22-2004

"JKop" <(E-Mail Removed)> wrote in message
news:r5UBc.2844$(E-Mail Removed)...
>
> 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.




 
Reply With Quote
 
Branimir Maksimovic
Guest
Posts: n/a
 
      06-22-2004

"JKop" <(E-Mail Removed)> wrote in message
news:r5UBc.2844$(E-Mail Removed)...
> 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.



 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
perl efficiency -- fastest grepping? Bryan Krone Perl 1 11-08-2004 11:15 PM
New Releases: Ultimate Rambo, Ultimate Oliver Stone & More 2 packs: Updated complete downloadable R1 DVD DB & info lists Doug MacLean DVD Video 0 09-10-2004 04:34 AM
Custom Paging Efficiency Joseph D. DeJohn ASP .Net 1 08-06-2003 06:24 PM
CISCO 1721 efficiency problem DarejDok \(Dariusz Dħbrowski\) Cisco 0 07-16-2003 07:15 AM
dataset efficiency question Trevor Hartman ASP .Net 0 07-03-2003 08:20 PM



Advertisments