12
votes

I was reading the constant chapter in Eckel and got mixed up in the part where Temporaries were explained . What I could get was that when we pass the reference to a function , the compiler creates a temporary which is a const object and so we can't modify it even if we pass a reference as

f(int &a){}

Now I tried to look at some other references for Temporaries online and got stuck up

http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr382.htm and

Are all temporaries rvalues in C++?

which prompted me that temporaries are much more than just passing references and creating const object for that inside the functions.Now I could get something out of these two links but could not say that I have understood the working , functionality and use of temporaries as a whole. It would be really helpful if someone could explain the concept of temporaries. Thanks in advance.
The original example of bruce eckel is:

// 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());
} ///:~
4
References and temporaries are not directly related. It has to do with how the object is created/destroyed. Perhaps you better start again..Karthik T

4 Answers

5
votes

A temporary is an unnamed object (the results of some expressions), and is always an rvalue. Or perhaps one should better say that an expression which results in an rvalue is a temporary.

In C, rvalues/temporaries weren't really objects (in the sense the standard uses the word "object": something that is located in memory). Thus, for example, they weren't cv-qualified (an expression like 3 + 5 has type int, and not int const, and cv-qualifiers are ignored on function return values) and you can't take their address (because they aren't in memory, they don't have an address). In C++, the issue is clouded by class types: you can call a member function on an rvalue, and that member function will have a this pointer, which means that even rvalues (of class type) must have an address in memory, and that cv-qualifications have meaning, since if the return type is const, you can't call a non-const function on it.

In the end, although the concepts of rvalue and temporary are very closely related, the C++ standard uses the words in slightly different ways. The results of an expression are either an rvalue or an lvalue (C++11 adds other possibilities, but you can ignore them until you're an expert), and this distinction concerns all types. When the C++ standard speaks of a temporary, it is an rvalue which is (or has become) an object. For the most part, these have class type; there are very few cases where you would have a temporary which is not of class type in well written code, except where templates are involved. The distinction is important, because even if the built-in & operator is illegal on an rvalue, rvalues of class type have a defined "lifetime" and a memory address. That lifetime is until the end of the full expression. So when class types are concerned, the difference between a temporary and a named value is mainly that the temporary doesn't have a name, and it has a different lifetime. The lifetime of a class type is important for two reasons: first, it determines when the destructor is called, and second, if the object "leaks" a pointer to internal data, e.g. like std::string::c_str(), it determines how long this pointer may be valid.

Finally, I mention templates because that is about the only time you would have a const reference to a non-class type. The usual convention for in arguments is pass by value for non-class types, and by const reference for class types; the author of the template, however, doesn't know whether T will be a class types or not, and in most cases, will define his function to take a T const&. This will be, in practice, about the only time you'll end up with a temporary object of non-class type (and if the template saves the address of the argument, you may have problems).

4
votes

In C++ temporaries are unnamed objects that compiler creates in various contexts. The typical uses include reference initialization, argument passing, evaluation of expressions (including standard type conversions), function returns, and exceptions (throw expressions).

As from this link:

When a temporary object is created to initialize a reference variable, the name of the temporary object has the same scope as that of the reference variable. When a temporary object is created during the evaluation of a full-expression (an expression that is not a subexpression of another expression), it is destroyed as the last step in its evaluation that lexically contains the point where it was created.

There are exceptions in the destruction of full-expressions:

  1. The expression appears as an initializer for a declaration defining an object: the temporary object is destroyed when the initialization is complete.
  2. A reference is bound to a temporary object: the temporary object is destroyed at the end of the reference's lifetime.

If a temporary object is created for a class with constructors, the compiler calls the appropriate (matching) constructor to create the temporary object.

When a temporary object is destroyed and a destructor exists, the compiler calls the destructor to destroy the temporary object. When you exit from the scope in which the temporary object was created, it is destroyed. If a reference is bound to a temporary object, the temporary object is destroyed when the reference passes out of scope unless it is destroyed earlier by a break in the flow of control. For example, a temporary object created by a constructor initializer for a reference member is destroyed on leaving the constructor.

In cases where such temporary objects are redundant, the compiler does not construct them, in order to create more efficient optimized code. This behavior could be a consideration when you are debugging your programs, especially for memory problems.

Let us summarize it this way. These temporary objects can be created for the following reasons:

  1. To initialize a const reference with an initializer of a type different from that of the underlying type of the reference being initialized.

  2. To store the return value of a function that returns a user-defined type. These temporaries are created only if your program does not copy the return value to an object. Because the return value is not copied to another object, a temporary object is created. A more common case where temporaries are created is during the evaluation of an expression where overloaded operator functions must be called. These overloaded operator functions return a user-defined type that often is not copied to another object.

  3. To store the result of a cast to a user-defined type. When an object of a given type is explicitly converted to a user-defined type, that new object is constructed as a temporary object.

Let's consider the example:

class X {
   / / ...
public:
   / / ...
   X(int);
   X(const X&);
   ~X();
};

X f(X);

void g()
{
   X a(1);
   X b = f(X(2));
   a = f(a);
}

Here, an implementation might use a temporary in which to construct X(2) before passing it to f() using X’s copy-constructor; alternatively, X(2) might be constructed in the space used to hold the argument. Also, a temporary might be used to hold the result of f(X(2)) before copying it to b using X’s copy-constructor; alternatively, f()’s result might be constructed in b. On the other hand, the expression a=f(a) requires a temporary for the result of f(a), which is then assigned to a.

1
votes

Lets start with an example:

float f(int a);
int g(float b);
int main() { int a=10; std::cout << g(f(a)); }

The function calls are looking like this:

int ---f----> float ---g---> int

When compiler generates code for g(f(a)), it needs some memory to store the resulting float after f() has been called. This memory area is called a temporary. It's the memory between f() and g().

0
votes

Temporaries are objects that are "by-products" of a computation. They are not explicitly declared, and as their name implies, they are temporary. Still, you should know when the compiler is creating a temporary object because it is often possible to prevent this from happening.

As several answers int his post have in detail on what temporaries are, but i would like to add something related to "extra overhead" associated with them, but there are also ways we can avoid them.

  1. The most common place for temporaries to occur is in passing an object to a method by value. The formal argument is created on the stack. This can be prevented by using pass by address or pass by reference.

  2. Compilers may create a temporary object in assignment of an object. For example, a constructor that takes an int as an argument may be assigned an int. The compiler will create a temporary object using the int as the parameter and then call the assignment operator on the object. You can prevent the compiler from doing this behind your back by using the explicit keyword in the declaration of the constructor.

  3. When objects are returned by value, temporaries are often used. Methods that must return an object usually have to create an object to return. Since constructing this object takes time, we want to avoid it if possible. There are several ways to accomplish this.

    3.a. Instead of returning an object, add another parameter to the method which allows the programmer to pass in the object in which the programmer wants the result stored. This way the method won't have to create an extra object. It will simply use the parameter passed to the method. This technique is called Return Value Optimization (RVO).

Whether or not RVO will result in an actual optimization is up to the compiler. Different compilers handle this differently. One way to help the compiler is to use a computational constructor. A computational constructor can be used in place of a method that returns an object. The computational constructor takes the same parameters as the method to be optimized, but instead of returning an object based on the parameters, it initializes itself based on the values of the parameters.

  1. Temporaries can be avoided by using = operators. For example, the code

    a = b + c; could be written as a=b; a+=c;.