Returning by value (here we go again!)

J

JKop

AnyClass Blah()
{
AnyClass poo;

return poo;
}


As we all know, in the above, the compiler is entitled to:

1) Create the object "poo"
2) Create a copy of "poo"
3) Destroy "poo"
4) Return the copy

or

1) Create the object "poo"
2) Return "poo"


On account of the first choice, whether a copy is made or not, the copy
constructor must be public.

First of all, I think it would be handy if we had a new type of "return"
statement in C++, one which makes it so that the actual object is returned
(no copy made) and as such the copy constructor wouldn't need to be
public... allowing the likes of the following:

std::eek:stream Blah()
{
std::eek:stream poo;

return_actual_object poo;
}


Anyway...

To achieve this, at the moment I'm using auto_ptr, as follows:

auto_ptr<AnyClass> Blah()
{
auto_ptr<AnyClass> poo( new AnyClass );

return poo;
}

int main()
{
auto_ptr<AnyClass> const &milk = Blah();

//now work with it as I please!
//and if I want reference syntax:

AnyClass const& cheese = *milk;

cheese.MemberFunction();
}


The compiler still has the choice of copying the auto_ptr... but it's a hell
of a lot better than copying the entire object! Plus, I can return an
"std::eek:stream" by value... (kind of!).

AAnnyywwaayy... looking through a C++ reference today I was sort of shocked
to see:

basic_string::substr
basic_string substr(size_type off = 0,
size_type count = npos) const;
The member function returns an object whose controlled sequence is a copy of
up to count elements of the controlled sequence beginning at position off.


It returns by value! AAAAAAaaahhhhhhhh! throw Inefficiency(); !


-JKop
 
K

Karl Heinz Buchegger

JKop said:
First of all, I think it would be handy if we had a new type of "return"
statement in C++, one which makes it so that the actual object is returned
(no copy made) and as such the copy constructor wouldn't need to be
public... allowing the likes of the following:

std::eek:stream Blah()
{
std::eek:stream poo;

return_actual_object poo;
}

The problem with this is not the return.
The problem is that copying a stream object is forbidden.

As for your suggestion.
Now we have a simple rule: Every local object gets destroyed at the
end of the function. Your suggestion would make an exception to that.
What would be scope of that local object? In other words: when
does it get destroyed?
Anyway...

To achieve this, at the moment I'm using auto_ptr, as follows:

auto_ptr<AnyClass> Blah()
{
auto_ptr<AnyClass> poo( new AnyClass );

return poo;
}

Fine. In other words: You dynamically allocate the object
and ask the caller to free it. Fine. In order to help
the caller somewhat, you put an envelope around it. Also
fine.

[snip]
The compiler still has the choice of copying the auto_ptr... but it's a hell
of a lot better than copying the entire object! Plus, I can return an
"std::eek:stream" by value... (kind of!).

AAnnyywwaayy... looking through a C++ reference today I was sort of shocked
to see:

basic_string::substr
basic_string substr(size_type off = 0,
size_type count = npos) const;
The member function returns an object whose controlled sequence is a copy of
up to count elements of the controlled sequence beginning at position off.

It returns by value! AAAAAAaaahhhhhhhh! throw Inefficiency(); !

You should not be shocked.

string a = "abcd";
string b = a.substr(2) + "xyz";

In order for this to work, there is no other choice as to return a copy.
Return value optimization takes care of the rest.
 
H

Howard Hinnant

JKop said:
First of all, I think it would be handy if we had a new type of "return"
statement in C++, one which makes it so that the actual object is returned
(no copy made) and as such the copy constructor wouldn't need to be
public... allowing the likes of the following:

std::eek:stream Blah()
{
std::eek:stream poo;

return_actual_object poo;
}

I agree, at least partly. This can be achieved with move semantics:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm

which unfortunately is not part of C++ today. If you would like to see
move semantics become part of C++, make sure your national
representative on the C++ committee knows that.

Movable, but non-copyable types with non-cv qualified auto storage
should be returnable by value from functions. For example see:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm#Moving%
20from%20local%20values

std::streams could be made movable, and remain non-copyable.
Anyway...

To achieve this, at the moment I'm using auto_ptr, as follows:

auto_ptr<AnyClass> Blah()
{
auto_ptr<AnyClass> poo( new AnyClass );

return poo;
}

int main()
{
auto_ptr<AnyClass> const &milk = Blah();

//now work with it as I please!
//and if I want reference syntax:

AnyClass const& cheese = *milk;

cheese.MemberFunction();
}

AAnnyywwaayy... looking through a C++ reference today I was sort of shocked
to see:

basic_string::substr
basic_string substr(size_type off = 0,
size_type count = npos) const;
The member function returns an object whose controlled sequence is a copy of
up to count elements of the controlled sequence beginning at position off.

It returns by value! AAAAAAaaahhhhhhhh! throw Inefficiency(); !

A good compiler can optimize away this copy if the client is using
substr to construct another string. But if it is using substr to assign
into an existing string a temporary is still created.

It would be a lot more palatable with move semantics. The temporary
could still be created, but then it would be moved from, instead of
copied from.

-Howard
 
C

Chris Theis

JKop said:
AnyClass Blah()
{
AnyClass poo;

return poo;
}


As we all know, in the above, the compiler is entitled to:

1) Create the object "poo"
2) Create a copy of "poo"
3) Destroy "poo"
4) Return the copy

or

1) Create the object "poo"
2) Return "poo"

or optimize away the function call to Blah and create the AnyClass object at
the point of the function call instead. However, whether this can be done
and makes sense depends on the context.
On account of the first choice, whether a copy is made or not, the copy
constructor must be public.

Well, what´s the problem with that. Declaring the copy ctor
private/protected is an idiom that signals that no copy can be created.
First of all, I think it would be handy if we had a new type of "return"
statement in C++, one which makes it so that the actual object is returned
(no copy made) and as such the copy constructor wouldn't need to be
public... allowing the likes of the following:

std::eek:stream Blah()
{
std::eek:stream poo;

return_actual_object poo;
}

As Karl Heinz already mentioned there is the issue of scope and automatic
destruction, which makes absolutely sense. Otherwise we´re going into the
C#, Java etc. direction where garbage control ist needed. The pros & cons of
GC are a topic themselves.
Anyway...

To achieve this, at the moment I'm using auto_ptr, as follows:

auto_ptr<AnyClass> Blah()
{
auto_ptr<AnyClass> poo( new AnyClass );

return poo;
}

int main()
{
auto_ptr<AnyClass> const &milk = Blah();

//now work with it as I please!
//and if I want reference syntax:

AnyClass const& cheese = *milk;

cheese.MemberFunction();
}

So you´re using a pointer with transferable ownership but you´re heading in
the direction where you clutter your code with mostly unnecessary
constructions like that. With a proper design such things should rarely be
required.
The compiler still has the choice of copying the auto_ptr... but it's a hell
of a lot better than copying the entire object! Plus, I can return an
"std::eek:stream" by value... (kind of!).

BTW, copying streams is not allowed.
AAnnyywwaayy... looking through a C++ reference today I was sort of shocked
to see:

basic_string::substr
basic_string substr(size_type off = 0,
size_type count = npos) const;
The member function returns an object whose controlled sequence is a copy of
up to count elements of the controlled sequence beginning at position off.


It returns by value! AAAAAAaaahhhhhhhh! throw Inefficiency(); !

Hold you breath ;-) Returning by value instead of reference makes absolutely
sense in this case, as already pointed out by Karl Heinz. However, I´d
suggest that you read the respective chapter 23 in Scott Meyers "Effective
C++".

As he says:
"It all boils down to this: when deciding between
returning a reference and returning an object, your job is to make the
choice that does the right thing. Let your compiler vendors wrestle with
figuring out how to make that choice as inexpensive as possible"
 
E

E. Robert Tisdale

JKop said:
AnyClass Blah(void) {
AnyClass poo;
return poo;
}


As we all know, in the above, the compiler is entitled to:

1) Create the object "poo"
2) Create a copy of "poo"
3) Destroy "poo"
4) Return the copy

or

1) Create the object "poo"
2) Return "poo"


On account of the first choice, whether a copy is made or not,
the copy constructor must be public.

First of all, I think it would be handy if we had a new type of "return"
statement in C++, one which makes it so that the actual object is returned
(no copy made) and as such the copy constructor wouldn't need to be
public... allowing the likes of the following:

std::eek:stream Blah()
{
std::eek:stream poo;

return_actual_object poo;
}


Anyway...

To achieve this, at the moment I'm using auto_ptr, as follows:

auto_ptr<AnyClass> Blah()
{
auto_ptr<AnyClass> poo( new AnyClass );

return poo;
}

int main()
{
auto_ptr<AnyClass> const &milk = Blah();

//now work with it as I please!
//and if I want reference syntax:

AnyClass const& cheese = *milk;

cheese.MemberFunction();
}


The compiler still has the choice of copying the auto_ptr... but it's a hell
of a lot better than copying the entire object! Plus, I can return an
"std::eek:stream" by value... (kind of!).

AAnnyywwaayy... looking through a C++ reference today I was sort of shocked
to see:

basic_string::substr
basic_string substr(size_type off = 0,
size_type count = npos) const;
The member function returns an object whose controlled sequence is a copy of
up to count elements of the controlled sequence beginning at position off.


It returns by value! AAAAAAaaahhhhhhhh! throw Inefficiency(); !

You are confused.
cat main.cc
#include <iostream>

class AnyClass {
private:
// representation
int I;
public:
// operators
friend
std::eek:stream& operator<<(std::eek:stream& os, const AnyClass& a);
// constructors
AnyClass(int i = 0): I(i) {
std::cout << "AnyClass::AnyClass(int)" << std::endl;
}
AnyClass(const AnyClass& a): I(a.I) {
std::cout << "AnyClass::AnyClass(const AnyClass&)"
<< std::endl;
}
~AnyClass(void) {
std::cout << "AnyClass::~AnyClass(void)" << std::endl;
}
};

inline
std::eek:stream& operator<<(std::eek:stream& os, const AnyClass& a) {
return os << a.I;
}

AnyClass Blah(void) {
AnyClass poo;
return poo;
}

int main(int argc, char* argv[]) {
AnyClass a = Blah();
std::cout << "a = " << a << std::endl;
return 0;
}
g++ -Wall -ansi -pedantic -o main main.cc
./main
AnyClass::AnyClass(int)
a = 0
AnyClass::~AnyClass(void)

1. ) The copy constructor is *never* called,
2. ) AnyClass(int) initializes the return value directly and
3. ) Blah(void) initializes object a directly.

You can't write code that is more efficient than that.
 
J

JKop

1. ) The copy constructor is *never* called,

With your particular compiler, yes.
2. ) AnyClass(int) initializes the return value directly and

With your particular compiler, yes.
3. ) Blah(void) initializes object a directly.

With your particular compiler, yes.
You can't write code that is more efficient than that.

With your particular compiler, yes.


What I'm getting at is that when you return an object by value, under the
duress of the Standard, the compiler is entitled to make copies and destroy
originals.


-JKop
 
E

E. Robert Tisdale

JKop said:
With your particular compiler, yes.


With your particular compiler, yes.


With your particular compiler, yes.


With your particular compiler, yes.

What I'm getting at is that when you return an object by value,
under the duress of the Standard,
the compiler is entitled to make copies and destroy originals.

Correct.
The C++ standard specifies the language and not the implementation.
The quality of the implementation is governed by the marketplace.
You are expect, as a responsible consumer,
to shop for a compiler that performs as required.
There are probably quality C++ compilers for virtually every platform
that might be a target for the code that you are writing.
 
C

Chris Theis

JKop said:
With your particular compiler, yes.


With your particular compiler, yes.


With your particular compiler, yes.


With your particular compiler, yes.


What I'm getting at is that when you return an object by value, under the
duress of the Standard, the compiler is entitled to make copies and destroy
originals.

Of course it is and it makes good sense. If you return by value you actually
ask the compiler to create a copy - well, if it can avoid it the better. The
destruction of the original has nothing to do by return the object by value
but rather by reaching the end of scope.
Furthermore there are many cases where this behavior is absolutely desirable
as it's been pointed out for example by Karl Heinz. Check out Scott Meyers'
Effective C++ item 23.

Chris
 
J

josh

JKop said:
AnyClass Blah()
{
AnyClass poo;

return poo;
}

As we all know, in the above, the compiler is entitled to:

1) Create the object "poo"
2) Create a copy of "poo"
3) Destroy "poo"
4) Return the copy

or

1) Create the object "poo"
2) Return "poo"

You're not actually looking at what happens at the end of Blah, you're
looking at the boundary between Blah and its caller. The second method
only happens when the compiler is able to merge part of the caller with
Blah, ie the second case doesn't skip creating the copy, it skips
creating poo.

As for actual implementation, this would have to happen before Blah
returned, because if poo is to be copied it still needs to exist. But
using the copy constructor works in the general case, whereas skipping
it will only work if you only call Blah in specific ways. That is, how
Blah can be optimized depends on the code that calls Blah rather than
Blah's code itself.

In particular:
AnyClass tar;
try
{
tar = Blah();
}
catch (...)
{
}
will not work if the copy is skipped. If poo's constructor throws and
it's "returning the actual object", tar is left in an inconsistent state.

As opposed to:
AnyClass tar = Blah();
where tar cannot be constructed at all if the constructor throws poo.

I think that a new type of return that always returned the object rather
than copying would have some very complicated consequences.

I suppose if you were really desperate, you could simulate how a
theoretical compiler might do it with something like:

AnyClass &Blah(void *carrots)
{
AnyClass *poo = new(carrots) AnyClass;

return *poo;
}

void Cactus(void)
{
char cement[sizeof AnyClass];
AnyClass &tar = Blah(cement);

tar.MemberFunction();

tar.~AnyClass();
}

hm... probably. Except you'd want some sort of template to automate
that mess.
AAnnyywwaayy... looking through a C++ reference today I was sort of shocked
to see:

basic_string::substr
basic_string substr(size_type off = 0,
size_type count = npos) const;
The member function returns an object whose controlled sequence is a copy of
up to count elements of the controlled sequence beginning at position off.

It returns by value! AAAAAAaaahhhhhhhh! throw Inefficiency(); !

Yeah, std::string is like that. But the alternative is far more
complex. (check out the string classes used in Mozilla)

-josh
 
D

Duane Hebert

auto_ptr said:
{
auto_ptr<AnyClass> poo( new AnyClass );

return poo;
}


Maybe I'm being dense but std::auto_ptr gets
removed when it exits scope, no?
int main()
{
auto_ptr<AnyClass> const &milk = Blah();

How can this work if the memory is overwritten by something else?
 
J

JKop

Duane Hebert posted:
Maybe I'm being dense but std::auto_ptr gets
removed when it exits scope, no?

Correct. But a copy of it is made for it's destroyed. The copy is returned.

Furthermore, when you make a copy of an auto_ptr, when the original is
destroyed, it no longer calls "delete".
How can this work if the memory is overwritten by something else?


Wha?


-JKop
 
D

Duane Hebert

JKop said:
Duane Hebert posted:


Correct. But a copy of it is made for it's destroyed. The copy is returned.

Furthermore, when you make a copy of an auto_ptr, when the original is
destroyed, it no longer calls "delete".

Of course. Thanks.
 

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

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top