What are С++ temporaries?
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());
} ///:~
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:
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:
To initialize a const reference with an initializer of a type different from that of the underlying type of the reference being initialized.
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.
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
.
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, eg 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).
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().
链接地址: http://www.djcxy.com/p/69788.html上一篇: 我怎样才能用PHP解析一个JSON文件?
下一篇: 什么是С++临时对象?