How does a function actually return a value?

If I have a class A (which returns an object by value), and the two functions f () and g () have a difference only in their return variables:

class A
{
    public:
    A () { cout<<"constructor, "; }
    A (const A& ) { cout<<"copy-constructor, "; }
    A& operator = (const A& ) { cout<<"assignment, "; }
    ~A () { cout<<"destructor, "; }
};
    const A f(A x)
    {A y; cout<<"f, "; return y;}

    const A g(A x)
    {A y; cout<<"g, "; return x;}

main()
{
    A a;
    A b = f(a);
    A c = g(a);
}

Now when I execute the line A b = f(a);, it outputs:

copy-constructor, constructor, f, destructor, which understands perfectly well that the object y in f () is created directly at the ie destination in the memory cell of object b and the temporary ones are not involved.

While I execute the line A c = g(a);, it produces:

copy-constructor, constructor, g, copy-constructor, destructor, destructor,.

So, the question is why, in the case of g (), it is impossible for the object to be directly created in memory cell c, as happened when f () was called? Why does it call an additional constructor instance (which, I believe, is due to the participation of a temporary one) in the second case?

+5
source share
4

, g , . , 12.8p31, .

, , , ( call) .

( , ...), NRVO , ( , :)):

: NRVO

: elision

+3

, . , , (main ), , , g().

http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

-, Ive , , . , , : , ( - ) , .

+7

, , :

class A{
public:
    A(const char* cname) : name(cname){
        std::cout << "constructing " << cname << std::endl;
    }
    ~A(){
        std::cout << "destructing " << name.c_str() << std::endl;
    }
    A(A const& a){
        if (name.empty()) name = "*tmp copy*";
        std::cout 
            << "creating " << name.c_str() 
            << " by copying " << a.name.c_str() << std::endl;
    }
    A& operator=(A const& a){
        std::cout
            << "assignment ( "
                << name.c_str() << " = " << a.name.c_str()
            << " )"<< std::endl;
        return *this;
    }
    std::string name;
};

:

const A f(A x){
    std::cout 
        << "// renaming " << x.name.c_str() 
        << " to x in f()" << std::endl;
    x.name = "x in f()";
    A y("y in f()");
    return y;
}

const A g(A x){
    std::cout 
        << "// renaming " << x.name.c_str()
        << " to x in f()" << std::endl;
    x.name = "x in g()";
    A y("y in g()");
    return x;
}

int main(){
    A a("a in main()");
    std::cout << "- - - - - - calling f:" << std::endl;
    A b = f(a);
    b.name = "b in main()";
    std::cout << "- - - - - - calling g:" << std::endl;
    A c = g(a);
    c.name = "c in main()";
    std::cout << ">>> leaving the scope:" << std::endl;
    return 0;
}

- :

constructing a in main()
- - - - - - calling f:
creating *tmp copy* by copying a in main()
// renaming *tmp copy* to x in f()
constructing y in f()
creating *tmp copy* by copying y in f()
destructing y in f()
destructing x in f()
- - - - - - calling g:
creating *tmp copy* by copying a in main()
// renaming *tmp copy* to x in f()
constructing y in g()
creating *tmp copy* by copying x in g()
destructing y in g()
destructing x in g()
>>> leaving the scope:
destructing c in main()
destructing b in main()
destructing a in main()

- , . Copy Destructor, , . NRVO:

constructing a in main()
- - - - - - calling f:
creating *tmp copy* by copying a in main()
// renaming *tmp copy* to x in f()
constructing y in f()
destructing x in f()
- - - - - - calling g:
creating *tmp copy* by copying a in main()
// renaming *tmp copy* to x in f()
constructing y in g()
creating *tmp copy* by copying x in g()
destructing y in g()
destructing x in g()
>>> leaving the scope:
destructing c in main()
destructing b in main()
destructing a in main()

*tmp copy* y in f() , NRVO . , NRVO , . . ++: "return" :)

+4

it can (almost) optimize the entire call to the g () function, in which case your code looks like this:

A a;
A c = a;

as efficient as your code does. Now, when you pass aas a parameter by value (that is, not a link), the compiler should almost execute a copy there, and then it returns this parameter by value, it should execute another copy.

In the case of f (), when it returns what is actually temporary to an uninitialized variable, the compiler can see what it is safe to use cas storage for the internal variable inside f ().

0
source

All Articles