Return by const ref or value?

Hey everyone :) I just want to confirm if my understanding is correct.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Test{
   UserDefinedType a;
   public:
   Test(UserDefinedType aArg) : a(aArg) {}
   const UserDefinedType& getA_Version1() {return a;}
   UserDefinedType getA_Version2() {return a;}
}

int main(){
   Test tt(some UserDefinedType);
   UserDefinedType aV1 = tt.getA_Version1();
   UserDefinedType aV2 = tt.getA_Version2();
}


Ok don't worrie too much about syntax, I was just writing this up to help. Here is my understanding, let me know if I'm wrong/right:

getA_Version1 will return Test::a by reference (so so far there has been no copy constuctor called). Then the copy contructor on aV1 will copy a to aV1.

getA_Version2 will copy Test::a to the return object (copy constuctor), then this object will be copied to aV2 via the copy constructor.

So if I'm correct... returning by value in this case would cause two copy operations where as returning by reference will result in one copy operation.

I'm just checking that this is right. I know there are many instances where I need to pass back by value - I'm reading effective C++ atm - great book!

Thanks.

Nick.
Last edited on

getA_Version2 will copy Test::a to the return object (copy constuctor), then this object will be copied to aV2 via the assignment operator.


It has been a long time since I re-read Scott Meyers Effective C++ book but isn't below going to avoid calling the assignment operator ?

UserDefinedType aV2( tt.getA_Version2() );

versus

UserDefinedType aV2 = tt.getA_Version2();

Or above two are the same ?!?!
The two are the same - in that case. My understanding is (and I had mis-spoken in my original post):

UserDefinedType aV2 = tt.getA_Version2(); // Copy constructor

versus

UserDefinedType aV2; //Default constructor
aV2 = tt.getA_Version2(); // Assignment operator

Nick.
Last edited on
I use below code to test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class UserDefinedType {
public:
  UserDefinedType() { cout << "default ctor called!\n"; }
  UserDefinedType(const UserDefinedType&) { cout << "ctor called!\n"; }
  UserDefinedType& operator=(const UserDefinedType&) { cout << "assign called!\n"; }
};

class Test{
   UserDefinedType a;
   public:
   Test(UserDefinedType aArg) : a(aArg) {}
   const UserDefinedType& getA_Version1() {return a;}
   UserDefinedType getA_Version2() {return a;}
};

int main() {
  UserDefinedType aa;
  Test tt(aa);
  cout << "here onwards see\n";
  const UserDefinedType& aV1 = tt.getA_Version1(); //this does not call ctor or assign !!!
  UserDefinedType aV2 = tt.getA_Version2(); //this make ONE ctor call !!!
  UserDefinedType aV2 = tt.getA_Version2(); //this make ONE ctor call !!!
  return 0; 
}
Cool, good idea sohguanh. I changed the code a little bit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

using namespace std;

class UserDefinedType {
public:
  UserDefinedType() { cout << "default ctor called!\n"; }
  UserDefinedType(const UserDefinedType&) { cout << "ctor called!\n"; }
  UserDefinedType& operator=(const UserDefinedType&) { cout << "assign called!\n"; }
};

class Test{
   UserDefinedType a;
   public:
   Test(UserDefinedType aArg) : a(aArg) {}
   const UserDefinedType& getA_Version1() {return a;}
   UserDefinedType getA_Version2() {return a;}
};

int main() {
  UserDefinedType aa;
  Test tt(aa);
  cout << "Version1\n";
  const UserDefinedType aV1 = tt.getA_Version1();
  cout << "Version2\n";
  UserDefinedType aV2 = tt.getA_Version2();
  return 0;
}



OUTPUT:

default ctor called!
ctor called!
ctor called!
Version1
ctor called!
Version2
ctor called!

So version two is only calling the copy constructor once - where I thought it would call twice.

I'll look into it a bit more.

Nick.

Last edited on
closed account (z05DSL3A)
UserDefinedType aV2( tt.getA_Version2() ); (1)
versus
UserDefinedType aV2 = tt.getA_Version2(); (2)

Or above two are the same ?!?!


Technically they are different, (1) is direct-initialization and (2) is copy-initialization.
Direct-initialization is, generally, more flexible and more efficient.





Thanks for that Grey Wolf. Still why is only one copy constructor getting called when using tt.getA_Version2()? Is it compiler optimisation or something?
Last edited on
closed account (z05DSL3A)
Some compilers don't make temporary objects in such situations.

See:
http://en.wikipedia.org/wiki/Return_value_optimization
and maybe:
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.9
Excellent! Thanks for that.
And throughout all our try-out "assign called!" is not output. So it means when we read C++ code with A = B we do not immediately assume an assignment will take place isn't it?

Scott Meyers Effective C++ is a really good book and it is worth to note he actually mention all these back in the early 90's. His follow up More Effective C++ is also a good read.

It is so sad to note after these two great books, he releases no more further follow ups. I wonder what he is doing now instead :P

PS Btw I remember he wrote in the Foreword some of the problems is not from him originally. Those questions were raised by his students of different classes when he conduct training and classes so in a way we could think of Effective C++ is a book that comprises of multiple authors in another sense :P
Topic archived. No new replies allowed.