Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const reference to temporary and copying - C++

Please consider the following code,

struct foo
{
    foo()
    {
        std::cout << "Constructing!" << std::endl;
    }

    foo(const foo& f)
    {
        std::cout << "Copy constructing!" << std::endl;
    }

    ~foo()
    {
        std::cout << "Destructing.." << std::endl;
    }
};

foo get()
{
    foo f;
    return f;
}

int main()
{
    const foo& f = get();
    std::cout << "before return" << std::endl;
    return 0;
}

Output on MSVC

Constructing!
Copy constructing!
Destructing..
before return
Destructing..

Output of GCC

Constructing!
before return
Destructing..

The result which comes on MSVC looks incorrect.

Questions

  1. AFAIK, GCC produces the correct result here. Why MSVC is giving different results and why it is doing copy construction?
  2. const foo& f = get() and const foo f = get() produces same output because of return value optimization. In this case, which way of writing should be preferred?

Any thoughts..

like image 668
Navaneeth K N Avatar asked Mar 05 '10 04:03

Navaneeth K N


People also ask

Does const reference make a copy?

Not just a copy; it is also a const copy. So you cannot modify it, invoke any non-const members from it, or pass it as a non-const parameter to any function. If you want a modifiable copy, lose the const decl on protos .

Is it necessary to have const for a reference in copy constructor?

When we create our own copy constructor, we pass an object by reference and we generally pass it as a const reference. One reason for passing const reference is, we should use const in C++ wherever possible so that objects are not accidentally modified.

Does C support pass by constant reference?

C does not support references or passing by reference. You should use pointers instead and pass by address. Pass-by-value is efficient for primitive types, but does a shallow copy for structs. In C++ it makes a LOT of sense to pass objects by reference for efficiency.

Can references be const?

The grammar doesn't allow you to declare a “const reference” because a reference is inherently const . Once you bind a reference to refer to an object, you cannot bind it to refer to a different object.


3 Answers

Your MSVC build has no optimizations on. Turn them on, you'll get identical output for both.

GCC is merely performing, by default, RVO on your temporary. It's basically doing:

const foo& f = foo();

MSVC is not. It's making the foo in the function, copying it to the outside the function (ergo the copy-constructor call), destructing the inner foo, then binds the reference.

Both outputs are correct. RVO is one instance where the standard explicitly allows the observable behavior of the program to change.

like image 92
GManNickG Avatar answered Oct 18 '22 03:10

GManNickG


You are seeing the return value optimization, which is one kind of copy elision. Both programs are correct; the compiler is specifically given the option of eliminating a temporary which only serves to move data from one permanent object to another.

like image 36
Potatoswatter Avatar answered Oct 18 '22 02:10

Potatoswatter


The get() function is Constructing the local (print Constructing!), and returning a Foo object by value. The Foo object being returned must be created and is done so via copy construction (print Copy constructing!). Note that this is the object value assigned to the const foo & f in main.

Before that assignment takes place though, the function must return from get() and local variables (i.e. foo f; in get()) must be destroyed. (print 1st Destructing..) From there the program terminates (i.e. returns from main) then the object returned by get() and assigned to "f" is destroyed. (print 2nd Destructing...)

The reason you're seeing different output for the two compilers is that GCC is optimizing the return value for get() and is simply replacing const foo &f = get() to const foo &f = foo;

like image 1
RC. Avatar answered Oct 18 '22 02:10

RC.