Question about return type of function(use of &)

What is the difference between MyClass& function(){ MyClass a; return a;} and MyClass function(){MyClass a; return a}
if i were to write My Class b=function(); what would be assigned to b?with the first function? with the second one?
Thanks in advance
Last edited on
The first one returns a dangling reference (it binds a reference to the local variable 'a' and then 'a' is destroyed when the function ends). The behavior of MyClass b = function() is undefined.

The second one works and, conceptually, copy-constructs b from the copy of a that the function returned. In practice, it will default-construct b.
Last edited on
Ok, I understand this, but I have another question.I have to implement the + operator in a class created by me, to do it I write so:
1
2
3
4
5
6
MyClass operator + (MyClass& a)
{
MyClass temp;
.......
return temp;
}

and I implement the operator =
1
2
3
4
5
MyClass& operator = (MyClass& a)
{
........
return *this;
}

but my professor would like me to write so:
1
2
3
4
5
6
MyClass{MyClass temp;
My Class& operator + (Myclass& a)
{
......
return temp;
}

the professor support that my solution is wrong. Are you agree with the professor? how the + operator would be correct to write it?
the main is written so:
1
2
3
4
5
6
7
int main{
MyClass a,b,c;
a=new MyClass(attribute1);
b=new MyCLass(attribute2);
c=a+b;
return 0;
}


Thanks in advance
Neither you nor the professor are right.:)

The correct code will be

MyClass operator + ( const MyClass& a) const
{
MyClass temp;
.......
return temp;
}

MyClass& operator = ( const MyClass& a)
{
........
return *this;
}


how the + operator would be correct to write it?

whatever happens, operator+ has to return by value

While it's possible to write it as a member function (in which case it would look something like vlad's example:
1
2
3
4
5
MyClass MyClass::operator+(const MyClass& other) const {
    MyClass result = *this;
    result += other; // assuming you implemented operator+=
    return result;
}

, it is almost always better implemented as a non-member function:
1
2
3
4
5
6
MyClass operator+(const MyClass& lhs, const MyClass& rhs)
{
    MyClass result = lhs;
    result += rhs;
    return result;
}

or, if you want to take advantage of the optimizations offered by C++, take the left argument by value (so it can be moved in) and don't copy it again, which gives this canonical form:
1
2
3
4
5
MyClass operator+(MyClass lhs, const MyClass& rhs)
{
    lhs += rhs;
    return lhs; // or as a one-liner, return lhs += rhs;
}

...but that might fly way over the head of your professor
Last edited on
Also you could define the move-assignment operator

MyClass& operator = ( MyClass &&a)
{
........
return *this;
}


Ok Thanks to both!!!
Another 2 question:
1- I have tryed with non-member function, but the compiler says that the + operator must have 1 or 0 arguments. Why this?
2- When the function terminated, the return value is stored in the stack of the main(), will this value remain in memory until at the end of the main? if yes, then whenever the function return its value, the stack of the main fills.

Sorry for the many questions
> Also you could define the move-assignment operator

You could.
But if you are in the least bit sensible, don't define foundation operations of your own unless you have to.

Define them if and only if the operations implicitly declared (and synthesized where required) by the implementation would be semantically incorrect or inadequate.

As far as possible, not this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct A
{
    A( const std::string& s = "" ) : str(s) {}

    // user-defined destructor, copy constructor, move constructor,
    // copy assignment, move assignment

    ~A() noexcept {}

    A( const A& that ) : str(that.str) {}

    A( A&& that ) noexcept : str( std::move(that.str) ) {}

    A& operator= ( const A& that ) { str = that.str ; return *this ; }

    A& operator= ( A&& that ) noexcept
    { str = std::move(that.str) ; return *this ; }

    // ...

    std::string str ;
};


But this:
1
2
3
4
5
6
7
8
9
10
11
struct B
{
    B( const std::string& s = "" ) : str(s) {}

    // imlicitly declared destructor, copy constructor, move constructor,
    // copy assignment, move assignment

    // ...

    std::string str ;
};


B is simpler and more readable, has a much higher chance of being correct and is at least as efficient. If we later modify the class (say add a new non-static member std::vector<int> numbers ;), we just have to add the member in B. In A, the user-defined copy and move operations would have to be carefully rewritten.



> I have tryed with non-member function, but the compiler says that the + operator must have 1 or 0 arguments. Why this?

A non-member operator+() must have either one (for the unary + operator) or two (for the binary + operator) arguments.

A member operator+() must have either zero (for the unary + operator) or one (for the binary + operator) explicit argument.



> the return value is stored in the stack of the main(),
> will this value remain in memory until at the end of the main?

For a function that returns by value (that is does not return a reference), the result of the function is a "pure rvalue" or prvalue. It need not be stored anywhere at all (it need not be associated with an object). If it is stored somewhere as an anonymous temporary object, that temporary is destroyed as soon as it is no longer required.
Another question: I have written a Class that implements an doubly linked list, each node of List is in heap memory(i have used new),in destructor method of this Class all nodes are deleted through a procedure. If I create an object within a function and insert into it a new Node:
1
2
3
4
5
6
7
function  MyClass a()
{
MyClass temp;
Node element=new Node();
temp.start=element;
return temp;
}

return by value

In the main program I copy the content of the result of the function in another object:
1
2
3
4
int main()
{
MyClass object=function();
}


As you said before, the object "temp" is allocated temporarily in stack memory, when this object(temp) is destroyed it will call the destructor method?

I hope I was understandable
Thanks to all
As you said before, the object "temp" is allocated temporarily in stack memory, when this object(temp) is destroyed it will call the destructor method?


Yes. In this example, both 'temp' and 'object' will have their destructor run. So your 'MyClass' class, in this case, will need a copy ctor and/or a move ctor.
> the object "temp" is allocated temporarily in stack memory,
> when this object(temp) is destroyed it will call the destructor method?

If an object "temp" with an automatic storage duration is created in the function, when the function returns, the object will be destroyed (its non-trivial destructor will be called).

In practice, RVO will apply, and the "temp" object would be elided.
http://en.wikipedia.org/wiki/Return_value_optimization

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <iostream>
#include <initializer_list>

struct list
{
    list() { std::cout << "default constructor\n" ; }

    list( std::initializer_list<int> il )
    {
        std::cout << "constructor from initializer_list\n" ;
        for( int v : il ) push_back(v) ;
    }

    list( const list& that )
    {
        std::cout << "copy constructor\n" ;
        for( node* n = that.first ; n ; n = n->next ) push_back( n->value ) ;
    }

    list( list&& that ) noexcept
    {
        std::cout << "move constructor\n" ;
        first = that.first ; that.first = nullptr ;
        last = that.last ; that.last = nullptr ;
    }

    ~list()
    {
        std::cout << "destructor\n" ;
        while(first) pop_back() ;
    }

    list& operator=( const list& that ) ; // TODO
    list& operator=( list&& that ) noexcept ; // TODO

    void push_back( int v )
    {
        if(last) { last->next = new node( v, last ) ; last = last->next ; }
        else first = last = new node(v) ;
    }

    void pop_back()
    {
        if( last != first ) { last = last->prev ; delete last->next ; last->next = nullptr ; }
        if( last && last == first ) { delete last ; last = first = nullptr ; }
    }

    struct node
    {
        node( int v ) : value(v) {}
        node( int v, node* p ) : value(v), prev(p) {}

        int value ;
        node* next = nullptr ;
        node* prev = nullptr ;
    };

    node* first = nullptr ;
    node* last = nullptr ;
};

list foo()
{
    std::cout << "in function foo\n" ;
    return { 1, 2, 3, 4, 5 } ;
}

list bar()
{
    std::cout << "in function bar\n" ;
    list temp ;
    for( int i = 1 ; i < 6 ; ++i ) temp.push_back(i) ;
    return temp ;
}

int main()
{
    {
        list a = foo() ;
        std::cout << "back in main\n" ;
        for( auto n = a.first ; n ; n = n->next ) std::cout << n->value << ' ' ;
        std::cout << '\n' ;
    }

    std::cout << "----------------------------\n" ;

    {
        list b = bar() ;
        std::cout << "back in main\n" ;
        for( auto n = b.first ; n ; n = n->next ) std::cout << n->value << ' ' ;
        std::cout << '\n' ;
    }
}

http://ideone.com/kK3cIf
> if you want to take advantage of the optimizations offered by C++,
> take the left argument by value (so it can be moved in) and don't copy it again
> MyClass operator+(MyClass lhs, const MyClass& rhs)
http://www.boost.org/doc/libs/1_54_0/libs/utility/operators.htm#symmetry is outdated, ¿how does it compare now?


PS:
@OP: Node element=new Node(); doesn't compile
Last edited on
JLBorges, thank you for the perfect code and also the site, ideone.com
Ok, I understand this. But my question is a bit different. Before i didn't say that I have implemented the copy-constructor method so:
My Class(MyClass& object)
{
this->start=new Node(object.start);
}
Returning to previous, when it runs the main:
1
2
MyClass a,b,c;
c=a+b;

In the order, the first function wich is executed is the operator+, then the copy-costructor method and lastly the assignment operator(=).
When te operator(+) finish, in the memory of the main function automatically it creates a new temporary object using copy constructor. After that, through the = operator, its contents are copied to the object(c), it (the object created automatically) is automatically destroyed. When it is destroyed is the destructor method executed?
Thanks to all
> When te operator(+) finish, in the memory of the main function automatically
> it creates a new temporary object using copy constructor.

No. Typically, because of RVO, the copy is elided; ie. no copy or move constructor is involved.
Instead, the anonymous temporary object is constructed in-place.

> After that, through the = operator, its contents are copied to the object(c)

Yes.

> it (the object created automatically) is automatically destroyed.

Yes.

> When it is destroyed is the destructor method executed?

Yes.

For any object with a non-trivial destructor, 'Destroy the object' is synonymous with 'execute its destructor'


Perhaps this would make things more clear:

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
65
66
67
68
69
70
71
#include <iostream>

struct A
{
    A( int ii ) : i(ii) { std::cout << "constructor i == " << i << '\n' ; }

    A( const A& that ) : i(that.i)
    { std::cout << "copy constructor i == " << i << '\n' ; }

    ~A() { std::cout << "destructor i == " << i << '\n' ; }

    //A( A& that ) : i(that.i)
    A( A&& that ) : i(that.i)
    { std::cout << "move constructor i == " << i << '\n' ; }

    A& operator= ( const A& that )
    { i = that.i ; std::cout << "copy assignment i == " << i << '\n' ; return *this ; }

    A& operator= ( A&& that )
    { i = that.i ; std::cout << "move assignment i == " << i << '\n' ; return *this ; }

    int i ;
};

A operator+ ( const A& a, const A& b )
{
    A temp( a.i + b.i ) ; // RVO applies, constructing 'temp'
    return temp ; // and then copying temp is elided
    // the function directly constructs the return value, 'temp' is elided
}

int main()
{
    A a(6) ; // constructor i == 6
    A b(8) ; // constructor i == 8
    A c(0) ; // constructor i == 0
    std::cout << "------------------------------\n" ;

    c = a+b ; // call operator+(a,b), RVO applies
    // step one:
    // construct anonymous temporary 'in place' - constructor i == 14
    // no copy or move constructor is involved

    // step two:
    // assign the anonymous temporary returned by operator+ to c
    // move assignment i == 14

    // step three:
    // destroy the anonymous temporary returned by operator+
    // destructor i == 14
    std::cout << "------------------------------\n" ;

    a.i = 100 ;
    b.i = 56 ;

    A d = a+b ; // call operator+(a,b), RVO applies
    // this function returns a prvalue ("pure rvalue")
    // a prvalue need not be associated with any object, it can be elided
    // therefore, construct d 'in place'
    // construct d - constructor i == 156
    // no copy or move constructor is involved
    std::cout << "------------------------------\n" ;

    std::cout << "press enter to destroy 'a', 'b', 'c' and 'd' and then quit\n" ;
    std::cin.get() ;

    // destroy d - destructor i == 156
    // destroy c - destructor i == 14
    // destroy b - destructor i == 56
    // destroy a - destructor i == 100
}

http://ideone.com/PF1O7q
Last edited on
>>reply to JLBorges:
I have copied your code and I have executed it but the compiler give me error about the move assignment([Error] expected ',' or '...' before '&&' token). Why this?? Is my compiler different from your??
Thanks very much to previous reply
> expected ',' or '...' before '&&' token). Why this?? Is my compiler different from your??

&& (rvalue reference) is a C++11 feature.

Try compiling with the std=c++11 or std=c++0x compiler option.

Or just leave move semantics aside for the moment, and test this code instead:

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>

struct A
{
    A( int ii ) : i(ii) { std::cout << "constructor i == " << i << '\n' ; }

    A( const A& that ) : i(that.i)
    { std::cout << "copy constructor i == " << i << '\n' ; }

    ~A() { std::cout << "destructor i == " << i << '\n' ; }

    A& operator= ( const A& that )
    { i = that.i ; std::cout << "copy assignment i == " << i << '\n' ; return *this ; }

    int i ;
};

A operator+ ( const A& a, const A& b )
{
    A temp( a.i + b.i ) ; // RVO applies, constructing 'temp'
    return temp ; // and then copying temp is elided
    // the function directly constructs the return value, 'temp' is elided
}

int main()
{
    A a(6) ; // constructor i == 6
    A b(8) ; // constructor i == 8
    A c(0) ; // constructor i == 0
    std::cout << "------------------------------\n" ;

    c = a+b ; // call operator+(a,b), RVO applies
    // step one:
    // construct anonymous temporary 'in place' - constructor i == 14
    // no copy or move constructor is involved

    // step two:
    // assign the anonymous temporary returned by operator+ to c
    // assignment i == 14

    // step three:
    // destroy the anonymous temporary returned by operator+
    // destructor i == 14
    std::cout << "------------------------------\n" ;

    a.i = 100 ;
    b.i = 56 ;

    A d = a+b ; // call operator+(a,b), RVO applies
    // this function returns a prvalue ("pure rvalue")
    // a prvalue need not be associated with any object, it can be elided
    // therefore, construct d 'in place'
    // construct d - constructor i == 156
    // no copy or move constructor is involved
    std::cout << "------------------------------\n" ;

    std::cout << "press enter to destroy 'a', 'b', 'c' and 'd' and then quit\n" ;
    std::cin.get() ;

    // destroy d - destructor i == 156
    // destroy c - destructor i == 14
    // destroy b - destructor i == 56
    // destroy a - destructor i == 100
}

http://ideone.com/GG4yfw
Last edited on
I delete the move assignmente operatore and now the program function. Your idea was convenient to see if each object were successfully deleted. In my program all objects created are deleted properly. To see instead of printing the id, I printed out the memory address of each object ie:
[code]A(int ii){cout<<this}[/code
Thank you very much for the help you have given me
I delete the move assignmente operatore and now the program function. Your idea was convenient to see if each object were successfully deleted. In my program all objects created are deleted properly. To see instead of printing the id, I printed out the memory address of each object ie:
A(int ii){cout<<this}
Thank you very much for the help you have given me
Topic archived. No new replies allowed.