-1

I was going through the following question :

Temporary objects - when are they created, how do you recognise them in code?

In the code block of the question above , :which goes as follow :

//: C08:ConstReturnValues.cpp
// Constant return by value
// Result cannot be used as an lvalue

class X {
   int i;
public:
   X(int ii = 0);
   void modify();
};

X::X(int ii) { i = ii; }

void X::modify() { i++; }

X f5() {
   return X();
}

const X f6() {
   return X();
}

void f7(X& x) { // Pass by non-const reference
   x.modify();
}

int main() {
   f5() = X(1); // OK -- non-const return value
   f5().modify(); // OK
// Causes compile-time errors:
//! f7(f5());
//! f6() = X(1);
//! f6().modify();
//! f7(f6());
}

I could not identify what exactly is the following part in it :

 X f5() {
       return X();
    }

    const X f6() {
       return X();
    }

    void f7(X& x) { // Pass by non-const reference
       x.modify();
    }

What is happening in the above part ?

I think the part :

X f5() {
           return X();
        }

declares and defines a function f5 which returns an object of class X . However I am not sure about the part

return X()

Is it declaring an object of type X using a constructor without any arguments ?

Is my thinking about the above two code snippets correct or these are something different and are known by some term or concept in C++ , if yes what concept to read into for understanding that ?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • _"declares and defines a function f5 which returns an object of class X"_ That's exactly what it does, yes. _"s it declaring an object of type X using a constructor without any arguments ?"_ It creates a temporary instance of `X` and returns it. A copy will be most probably eliminated by the compiler for optimization. – πάντα ῥεῖ Aug 05 '18 at 13:13
  • @πάνταῥεῖ : Was the last line in your answer an explanation for that code instead of copy constructor ? – Kanishk Viman Aug 05 '18 at 14:40
  • It was a comment, not an answer. But you may look up _copy elision_ to better understand what would happen there. – πάντα ῥεῖ Aug 05 '18 at 14:43
  • You probably want to revise some basics about object construction and functions if that's the part you're confused by. You'll likely struggle with copy elision, temporary objects, copy constructors, and move constructors unless you have those basics well understood – JMAA Aug 05 '18 at 15:09

1 Answers1

1

Things happening in code here:

  1. The below code snippet says: "f5" is function which takes "void" as input argument and returns the newly constructed object of class "X" created by calling custom constructor with default input parameter i.e., It acts as default constructor here.
X f5() {
 return X(); // here it gives a call to X(0); default class "X" constructor
}
  1. Why X(0) is equivalent to your default constructor X() ?
    Reason: Since, you have overridden default class constructor with a parameterized construct with X(int ii = 0); with ii = 0as default argument.

  2. In main at f5() = X(1);

    • Custom constructor for Class X, gets called with "1" as input. Creating a new temporary object.
    • f5() calls the custom constructor with default input parameters as explained in point 2 and point 3.
    • There are some other things happening too. but lets ignore here for the sake of simplicity.

Bottom line: Yes temporary object gets created here.

  1. The below code snippet says the function 'f6()' returns a newly constructed by calling custom constructor with defaulted argument parameter. i.e., X::(0).
const X f6()
{
   return X();
}
  • The only difference between between f5() and f6() is f6() returns constant object.
  • Any kind of modification cannot be carried on the object created 'f6()' directly. Trying to achieve will result in compilation error. ex: f6().modify(); will through compiler error. and same on f5.modify() will work fine as its non-constant.
  • To make modification on object created by f6() you need to assign it to non-constant object of class 'X'.

Bottom line: Yes temporary object get created here but of constant type of X

  1. The below code snippet says the function 'f7' returns nothing (void) and takes non-constant reference to the Class 'X' objects.
void f7(X& x)
{
 x.modify();
}
  • Function f7 will modify any non-constant object which is passed to it. As it takes them by reference. It directly works on the memory of the original object. Similar to constant pointer to Class.

Bottom line: No temporary objects are created. Operation is directly carried on previously existing object.

Rule of thumb to find places where temporary objects are being created:
- Where ever you return by value or pass by value temporary objects gets created.
- Hence, return by reference and pass by reference are preferred. It doesn't creates any temporary objects. It is faster and memory friendly.
- When ever you are passing large data, Give preference to pass by reference / return by reference. Since it is faster and memory friendly.

Below is the code, with some extra prints and assignment operator. Compile and Run it. For further understandings.

//g++  5.4.0

#include <iostream>
//: C08:ConstReturnValues.cpp
// Constant return by value
// Result cannot be used as an lvalue

class X {
   int i;
public:
   X(int ii = 0);
   int getData(){return i;}
   void modify();
};

X::X(int ii) { std::cout << "Calling constructor as X::X("<< ii << ")"<< std::endl; i = ii; }

void X::modify() { std::cout << "X::modify()" << std::endl; i++; }

X f5() {
    std::cout << "f5()" << std::endl;
   return X();
}

const X f6() {
   std::cout << "f6()" << std::endl;
   return X();
}

void f7(X& x) { // Pass by non-const reference
   std::cout << "f7()" << std::endl;
   x.modify();
}

int main() {
    X obj0;    // Calls the constructor with defaulted argument 
    X obj1(2); // Calls the constructor with 2 as input
    f5();      // Calls the constructor with defaulted argument 
    std::cout << "obj1 i values before call to f7() : " << obj1.getData() << std::endl;
    f7(obj1); // Passing obj1 --- value of 'i' will get modified in for obj1 as we are passing it by reference
    std::cout << "obj1 i values after call to f7() : " << obj1.getData() << std::endl;
    f6();     // Will work fine
    //f6().modify(); // uncommenting it Will cause compiler error as you are trying to modify constant object
}