constant temporary items as returnvalue

Hey, back to the forums again with a mind boggling question. I'm trying to tackle operator overloading and wrote the following simple code below.

I made two variations on a operator+ to compare the behaviour.
One is a class member, the other is a friend. I (#)defined friends and members to decide which one I'm going to use. cancel out the definition of one of the two will trigger the other to work.

The below code works fine, and both generate identical output.

but I don't understand the following:
Why is it possible for the friend function to operate the chain: n = a + b + c while I made it return a const Object.

as I understand it:
before temporary_1 is stored into n,
the x of temporary_1 first contains 10, and later it's changed to 42, shouldn't that be impossible for a const Object?

If I try to do the same with the member version I get an error, as expected. Only if I don't use the returnvalue is a const Object allowed (for example: n = a + b; )

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>

using namespace std;

//#define friends
#define members

//in header
class Object {
    static int temp_objectCounter;
    int x; 
    const char identity;
public:

    Object(int xx = 0, const char c=' ') : x(xx), identity(c) { 
        if (identity != ' ') printf("Object %c created\n", identity); 
        else printf("Object temporary_%d created\n", ++temp_objectCounter);
    }
    Object(const Object& obj) : x(obj.x), identity('n') { cout << "copy constructor called\n";}
    ~Object() { 
        if (identity != ' ') printf("Object %c destroyed\n", identity); 
        else printf("Object temporary_%d destroyed\n", temp_objectCounter--); 
    }
#ifdef friends
    friend const Object
    operator+(const Object& left,
              const Object& right);
#endif

#ifdef members   
    Object operator+ (const Object& right);
#endif    
    void print();
};
//-----

//in class file------------------------

int Object :: temp_objectCounter = 0; 

#ifdef members 
inline Object Object::operator+ (const Object& right) {
   printf("%c & %c called to perform %d + %d\n", identity, right.identity, x, right.x);
    return x + right.x;
}
#endif

#ifdef friends
const Object operator+ (const Object& left, const Object& right) {
    printf("%c & %c called to perform %d + %d\n", left.identity, right.identity, left.x, right.x);
    return left.x + right.x;
}
#endif

inline void Object::print() {
    printf("value of %c is %d\n", identity, x);
}//-------------------------------------


int main() {
    Object a(2, 'a'), b(8, 'b'), c(32, 'c');
    Object n = a + b + c;
    n.print();
}



Object a created
Object b created
Object c created
a & b called to perform 2 + 8
Object temporary_1 created
  & c called to perform 10 + 32
Object temporary_2 created
Object temporary_2 destroyed
value of   is 42
Object temporary_1 destroyed
Object c destroyed
Object b destroyed
Object a destroyed



I hope someone would be so kind to clarify this for me. I spend a lot time trying to force C++ to visualize the steps for me, and still it's not clear to me :S
operator+ should never change the objects passed to it and it doesn't happen here either.
You're printing the wrong value in the destructor. You're printing temp_objectCounter, when it should be the value temp_objectCounter had when you created the object.
In reality, temporary_1 is destroyed before temporary_2 (also, note that thanks to RVO having taken place here, temporary_2 is actually n).
Last edited on
Thanks, my method wasn't clear indeed. I changed it to display addresses of this now and it shows exactly what you say. but.....

Still, my question remains, why can the friend function perform the chain (while returning a const object) and why is the member function not allowed to?

(yep, and i thought RVO was turning temporary_1 into n, thanks for showing that is actually temporary_2)
Last edited on
Also the operator+ are returning an integer not an object.
The compiler is using this constructor:
1
2
3
4
Object(int xx = 0, const char c=' ') : x(xx), identity(c) { 
        if (identity != ' ') printf("Object %c created\n", identity); 
        else printf("Object temporary_%d created\n", ++temp_objectCounter);
    }


as a converting constructor, to create an object from this integer.

This can be proved by changing the constructor type to explicit
and you program will fail to compile.
1
2
3
4
5
explicit Object(int xx = 0, const char c=' ') : x(xx), identity(c)  //explicit constructor
{ 
        if (identity != ' ') printf("Object %c created\n", identity); 
        else printf("Object temporary_%d created\n", ++temp_objectCounter);
    }
Still, my question remains, why can the friend function perform the chain (while returning a const object)

Why shouldn't it? operator+ is first called with a and b. The function returns a temporary object with the sum. operator+ is then called again with the temporary object and c and again the sum is returned. None of the objects are ever changed, so it does not matter whether they are const or not.

and why can't is the member function not allowed to?

Because you did not declare the member function as const - therefore you cannot call it for const objects. Change it to the following:
const Object operator+ (const Object& right) const;
Last edited on
With regard to the const thing the member function would have to be designated as a const member function.
1
2
3
4
5
6
7
8
9
10
#ifdef members   
    const Object operator+ (const Object& right) const;
#endif 

#ifdef members 
inline const Object Object::operator+ (const Object& right) const {
   printf("%c & %c called to perform %d + %d\n", identity, right.identity, x, right.x);
    return x + right.x;
}
#endif    

In C++ you can't call a non-const member function on a const object - so
making the function a const member function clears that up.

The global friend overload function did not have this problem it is not a member function (no this pointer and/or constancy to wory about)
and you had said that both leftside and right side objects are constant anyway in the paramer declaration.
1
2
3
4
5
#ifdef friends
    friend const Object
    operator+(const Object& left,
              const Object& right);
#endif 

!!!

Because you did not declare the member function as const - therefore you cannot call it for const objects. Change it to the following:
const Object operator+ (const Object& right) const;


That's it! Never crossed my mind to declare the function itself as const. I get it now, thanks a ton!


@gulkan
That's quite interesting. I've browsed through my book and see this is discussed later on, this technique has been quite the brainstress for me so far.
Topic archived. No new replies allowed.