What are C++ temporaries?
Asked Answered
A

4

13

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());
} ///:~
Armidaarmiger answered 28/2, 2013 at 7:59 Comment(1)
References and temporaries are not directly related. It has to do with how the object is created/destroyed. Perhaps you better start again..Syllabary
F
5

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.

Ferruginous answered 28/2, 2013 at 8:12 Comment(19)
What does the full expression part means here?Armidaarmiger
C++ does not have stack (other but std::stack that is container adaptor) mentioned in standard.Valera
@Kavish Dwivedi: i added the example.Ferruginous
@Öö Tiib: Btw, stack unwinding is mentioned in the standard. So, implicitly C++ does have a stack.Ferruginous
@SergeyK.:when f(X(2)) is to return a non-const object , it must be copied from the temporary which is const but eckel says that we can't copy a const object to a non-const object .Armidaarmiger
@Kavish Dwivedi: the compiler can omit the copy preserving semantics.Ferruginous
@SergeyK.: so what compiler does here is copies the const temporary to a non-const object , destroys the temporary and then returns the non const object . Am I right?Armidaarmiger
When throw is performed then objects must be destroyed in exactly opposite order of creation that is why the event is called "stack unwinding" that does not imply there is stack in architecture.Valera
@Öö Tiib: LIFO is the stack in the algorithmic sense. It is not about implementation/architecture.Ferruginous
@SergeyK :What I said in my above comment am I right about that?Armidaarmiger
@Kavish Dwivedi: No. The lifetime of temporary can be extended and they survive past the original expression.Ferruginous
@SergeyK.: yes , I understand this(it depends on the lifetime of reference) but what I said about the assignment of const to non-const and then return the non-const was right?Armidaarmiger
@Kavish Dwivedi: Yes, but the details under the hood (move semantics) can vary on different compilers. It is allowed optimization to move an object out of function.Ferruginous
Two points: first, I think you have an error---you say that a temporary is destructed when it goes out of scope: temporaries don't have scope (since they don't have names), and are destructed (normally) at the end of the full expression. Second, although there are rules which extend the temporaries lifetime in certain cases, can you think of a case where this would occur in practice (in well written code)?Bitchy
@James Kanze: #9468372Ferruginous
And with regards to your last point 2: you are assuming RVO. Formally, the return value of a function is always a temporary (when it has class type).Bitchy
@SergeyK. And what does that answer. It presents a few very artificial examples, which have nothing to do with anything anyone would reasonably write in an actual program. (If you're writing a compiler, you have to be concerned with such issues, and you need such code in your test suite, because the standard defines it in a specific way, and you want to conform. Otherwise, I think you can ignore it.)Bitchy
@James Kanze: it does not matter - it is in the standard.Ferruginous
@SergeyK. But it doesn't apply to anything that I wrote.Bitchy
B
6

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).

Bitchy answered 28/2, 2013 at 9:18 Comment(0)
F
5

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.

Ferruginous answered 28/2, 2013 at 8:12 Comment(19)
What does the full expression part means here?Armidaarmiger
C++ does not have stack (other but std::stack that is container adaptor) mentioned in standard.Valera
@Kavish Dwivedi: i added the example.Ferruginous
@Öö Tiib: Btw, stack unwinding is mentioned in the standard. So, implicitly C++ does have a stack.Ferruginous
@SergeyK.:when f(X(2)) is to return a non-const object , it must be copied from the temporary which is const but eckel says that we can't copy a const object to a non-const object .Armidaarmiger
@Kavish Dwivedi: the compiler can omit the copy preserving semantics.Ferruginous
@SergeyK.: so what compiler does here is copies the const temporary to a non-const object , destroys the temporary and then returns the non const object . Am I right?Armidaarmiger
When throw is performed then objects must be destroyed in exactly opposite order of creation that is why the event is called "stack unwinding" that does not imply there is stack in architecture.Valera
@Öö Tiib: LIFO is the stack in the algorithmic sense. It is not about implementation/architecture.Ferruginous
@SergeyK :What I said in my above comment am I right about that?Armidaarmiger
@Kavish Dwivedi: No. The lifetime of temporary can be extended and they survive past the original expression.Ferruginous
@SergeyK.: yes , I understand this(it depends on the lifetime of reference) but what I said about the assignment of const to non-const and then return the non-const was right?Armidaarmiger
@Kavish Dwivedi: Yes, but the details under the hood (move semantics) can vary on different compilers. It is allowed optimization to move an object out of function.Ferruginous
Two points: first, I think you have an error---you say that a temporary is destructed when it goes out of scope: temporaries don't have scope (since they don't have names), and are destructed (normally) at the end of the full expression. Second, although there are rules which extend the temporaries lifetime in certain cases, can you think of a case where this would occur in practice (in well written code)?Bitchy
@James Kanze: #9468372Ferruginous
And with regards to your last point 2: you are assuming RVO. Formally, the return value of a function is always a temporary (when it has class type).Bitchy
@SergeyK. And what does that answer. It presents a few very artificial examples, which have nothing to do with anything anyone would reasonably write in an actual program. (If you're writing a compiler, you have to be concerned with such issues, and you need such code in your test suite, because the standard defines it in a specific way, and you want to conform. Otherwise, I think you can ignore it.)Bitchy
@James Kanze: it does not matter - it is in the standard.Ferruginous
@SergeyK. But it doesn't apply to anything that I wrote.Bitchy
O
1

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().

Osanna answered 28/2, 2013 at 8:11 Comment(7)
here also whatever is stored as a temporary is constant ? I thought it happens only when we do a reference passing.Armidaarmiger
The standard does not speak of a temporary here, only a rvalue. The results of f and g are not objects, and do not (observably) occupy memory---they don't have an address, aren't cv-qualified, etc. (If you declare float const f(), the return type is float, not float const.)Bitchy
@KavishDwivedi Nothing is stored here, so whether it is const or not is irrelevant. If the return type is a class type, then const becomes relevant, and the type is const if and only if it was declared const.Bitchy
@james kanze: isn't it just some optimization whether you put the float to a register or keep it in memory? Nonoptimized would always keep it in memory, and temporary would be the memory area.Osanna
@Osanna In C, where the concept originated, the point of view was the opposite: an rvalue would not be in memory (thus no &) unless there was no way of doing otherwise. Abstractly viewed, it was just a "value", which wasn't put anywhere. (In the case of integral literals, for example, it would usually be part of a machine instruction.) Obviously, even in C, if the return type is something like struct { char x[1000000]; }, it will be in memory, but the language (C) still treats it as if it wasn't. It's only when you need a this pointer that it has to be in memory.Bitchy
@Osanna And neither C nor C++ speak of a temporary here. The word temporary doesn't even occur in C99. In C++, the lifetime of temporaries is discussed in §12.2, which is about class types. An rvalue of non-class type is a value, not an object (and a temporary is an object).Bitchy
So, if I change float to MyObject, it'll fix the answer?Osanna
E
0

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;.

Electroluminescence answered 28/2, 2013 at 8:27 Comment(6)
Practically all of this answer comes under the heading of premature optimization.Bitchy
@James Only Engineer/Developer experience and knows the overhead of temporaries, for a marketing guy this is just something which is not to be worried. But this is not a new topic and there has been extensive research on the way to avoid these temporaries... en.wikibooks.org/wiki/Optimizing_C%2B%2B/Code_optimization/… en.wikibooks.org/wiki/More_C%2B%2B_Idioms/… tantalon.com/pete/cppopt/final.htmElectroluminescence
docs.oracle.com/cd/E19205-01/819-5267/bkahr/index.html docs.oracle.com/cd/E18659_01/html/821-1383/bkahr.htmlElectroluminescence
@Kavish Dwivedi: You can find the example for 3.a on informit.com/guides/content.aspx?g=cplusplus&seqNum=199 due to char limitation i cant share here, for any question please do share...i would suggest to read whole especially "Return Value Optimization" Which should clear 3.aElectroluminescence
@Electroluminescence Only an amateur worries about such issues until they really are issues. A professional engineer writes the cleanest code possible in the circumstances where he works. (And from experience: I've worked on a lot of performance critical applications, and we stil regularly return std::vector by value.)Bitchy
@ James Kanze I am not sure what type of projects you worked on, but i work in embedded domain, and we cant tolerate "pass by value" in any circumstance! we care about each cycle CPU and utilize it in best way, we cant waste cycles on these temporaries. So we avoid them in any possible ways we could...Electroluminescence

© 2022 - 2024 — McMap. All rights reserved.