449,307 Members | 2,075 Online Need help? Post your question and get tips & solutions from a community of 449,307 IT Pros & Developers. It's quick & easy.

# Check if an argument is temporary

 P: n/a Hi all, I was wondering if it is possible if you can check in a function if one of the arguments is temporary. What I mean is the following. A is a class, foo is a function returning a class and bar is a function using A is an argument returning something else class A; A foo(void); int bar(const A&); int main(void) { std::cout << bar(foo()); return 0; } In the above piece of code, the object returned by foo is temporary and destroyed after bar is finished with it. The question is, is it possible to distinguish in bar if its argument is such a temporary object or not? Regards Klaas Oct 28 '07 #1
17 Replies

 P: n/a Klaas Vantournhout

 P: n/a On Oct 29, 3:29 am, "Daniel T."

 P: n/a James Kanze wrote: The real question is why the OP wants to know. There's likely a solution to his real problem which doesn't require such knowledge. Hmm okay this is what I had in mind. Assume you have a class array class array { public: array(int N) : n(N) { if (n) a = new double[N]; else a = NULL; } ~array(void) { delete [] a; } array operator=(const array &A) { if (n != A.n) { delete [] a; a = new double [A.n]; } for (register int i=N-1; i >= 0; --i) a[i] = A.a[i]; return *this; } private double *a; int n; } Assume now we have the function array foo(void); which creates an enormous array Then the following operation array B; B = foo(); Creates a tremendous amount of overhead. A temporary array is created in foo(), this array is passed to operator= and there each element of foo is copied to B. However, it would be useful if operator= could notice if the object is temporary. Because then we could write if (temporary) { a = A.a; A.a = NULL; } When this happens, foo gets destroyed after operator=, but it is only a NULL pointer, the real date is still there and not delete with ~array(void); Of course I see a problem here there I defined operator=(const array &), which means that I can not change A.a its value! The simple solution would obviously be redefining array foo(void) to void foo(array &) but this is unfortunately not an option here. Of course the above would not be a problem if optimization flags of compilers check if content a memory address is just moved to an other address. I don't know any assembly here, but what I mean is the following. Assume address a is filled by function1 and contains temporary data, and this data is placed by function2 in address b. Then I don't know if optimization flags see this, and immediately store the data of function1 in address b, instead of going around over temporary a. Regards Klaas Oct 29 '07 #4

 P: n/a On Oct 29, 11:53 am, Klaas Vantournhout wrote: James Kanze wrote: The real question is why the OP wants to know. There's likely a solution to his real problem which doesn't require such knowledge. Hmm okay this is what I had in mind. Assume you have a class array class array { public: array(int N) : n(N) { if (n) a = new double[N]; else a = NULL; } ~array(void) { delete [] a; } array operator=(const array &A) { if (n != A.n) { delete [] a; a = new double [A.n]; } for (register int i=N-1; i >= 0; --i) a[i] = A.a[i]; return *this; } private double *a; int n; } Assume now we have the function array foo(void); which creates an enormous array Then the following operation array B; B = foo(); Creates a tremendous amount of overhead. A temporary array is created in foo(), this array is passed to operator= and there each element of foo is copied to B. However, it would be useful if operator= could notice if the object is temporary. Because then we could write if (temporary) { a = A.a; A.a = NULL; } When this happens, foo gets destroyed after operator=, but it is only a NULL pointer, the real date is still there and not delete with ~array(void); Make A::operator= take the argument by Value instead of reference, then swap 'this' and the argument: struct A { A(...) { allocate-and-initialize-p } A(const A& rhs) { allocate-p-and-intialize-from-rhs } A& operator=(A x) { swap(*this, x); return *this; } firend void swap(A& lsh, A& rhs) { std::swap(lsh.p, rhs.p); } private: whatever * p }; (you do not really need a friend swap, you could directly swap or pilfer the pointer inside operator=, but swap is generally useful). If the argument of operator= is an lvalue, it will perform a copy (and you would need to do it anyway), which will swaped into 'this' without making . If it is a temporary, like in this case: A foo(); ... A x; x = foo();+ the compiler is free (and any decent complier will actually do so) to construct the result of foo directly in the argument slot of A::operator=, skipping any intermediate temporary. Also note that an operator= implemented in term of swap has the nice side effect of being trivially exception safe (your is not, consider what would happen if 'operator new' throws), safe even in face of self assignment and does eliminate any code duplicated with the copy constructor. HTH, Giovanni P. Deretta Oct 29 '07 #5

 P: n/a On 2007-10-29 12:27, gpderetta wrote: On Oct 29, 11:53 am, Klaas Vantournhout wrote: >James Kanze wrote: The real question is why the OP wants to know. There's likely a solution to his real problem which doesn't require such knowledge. Hmm okay this is what I had in mind.Assume you have a class arrayclass array {public: array(int N) : n(N) { if (n) a = new double[N]; else a = NULL; } ~array(void) { delete [] a; } array operator=(const array &A) { if (n != A.n) { delete [] a; a = new double [A.n]; } for (register int i=N-1; i >= 0; --i) a[i] = A.a[i]; return *this; }private double *a; int n;}Assume now we have the functionarray foo(void);which creates an enormous arrayThen the following operationarray B;B = foo();Creates a tremendous amount of overhead.A temporary array is created in foo(), this array is passed to operator=and there each element of foo is copied to B.However, it would be useful if operator= could notice if the object istemporary.Because then we could writeif (temporary) { a = A.a; A.a = NULL;}When this happens, foo gets destroyed after operator=, but it is only aNULL pointer, the real date is still there and not delete with ~array(void); Make A::operator= take the argument by Value instead of reference, then swap 'this' and the argument: So you remove one copy and create a new. By taking the argument by value a copy must be made so nothing is won. The solution to the problem is called R-value References or Move Semantics and will be part of the next version of the standard. Using this you will be able to write move-constructors which transfers ownership from the temporary to the new object. -- Erik WikstrÃ¶m Oct 29 '07 #6

 P: n/a On Oct 29, 10:53 am, Klaas Vantournhout wrote: ... this sounds like Alexandrescu's move constructors. http://www.ddj.com/database/184403855 DS Oct 29 '07 #7

 P: n/a Erik Wikström

 P: n/a On 2007-10-29 13:45, Erik WikstrÃ¶m wrote: On 2007-10-29 12:27, gpderetta wrote: >On Oct 29, 11:53 am, Klaas Vantournhout wrote: >>James Kanze wrote:The real question is why the OP wants to know. There's likely asolution to his real problem which doesn't require suchknowledge.Hmm okay this is what I had in mind.Assume you have a class arrayclass array {public: array(int N) : n(N) { if (n) a = new double[N]; else a = NULL; } ~array(void) { delete [] a; } array operator=(const array &A) { if (n != A.n) { delete [] a; a = new double [A.n]; } for (register int i=N-1; i >= 0; --i) a[i] = A.a[i]; return *this; }private double *a; int n;}Assume now we have the functionarray foo(void);which creates an enormous arrayThen the following operationarray B;B = foo();Creates a tremendous amount of overhead.A temporary array is created in foo(), this array is passed to operator=and there each element of foo is copied to B.However, it would be useful if operator= could notice if the object istemporary.Because then we could writeif (temporary) { a = A.a; A.a = NULL;}When this happens, foo gets destroyed after operator=, but it is only aNULL pointer, the real date is still there and not delete with ~array(void); Make A::operator= take the argument by Value instead of reference,then swap 'this' and the argument: So you remove one copy and create a new. By taking the argument by value a copy must be made so nothing is won. The solution to the problem is called R-value References or Move Semantics and will be part of the next version of the standard. Using this you will be able to write move-constructors which transfers ownership from the temporary to the new object. It is an optimisation that the compiler is allowed to, but not required to do. Also, there are situations where it is not possible. Consider the following: class Foo {}; Foo bar(int i) { Foo f1, f2; if (i == 1) return f1; return f2; } int main() { Foo f; f = bar(1); } In this case I can not make my compiler omit the copy-construction. It is because of situations like this (among other things) that move semantics were added. -- Erik WikstrÃ¶m Oct 29 '07 #9

 P: n/a Klaas Vantournhout wrote: James Kanze wrote: The real question is why the OP wants to know. There's likely a solution to his real problem which doesn't require such knowledge. Hmm okay this is what I had in mind. Assume you have a class array class array { public: array(int N) : n(N) { if (n) a = new double[N]; else a = NULL; } ~array(void) { delete [] a; } array operator=(const array &A) { if (n != A.n) { delete [] a; a = new double [A.n]; } for (register int i=N-1; i >= 0; --i) a[i] = A.a[i]; return *this; } private double *a; int n; } Assume now we have the function array foo(void); which creates an enormous array Then the following operation array B; B = foo(); Creates a tremendous amount of overhead. True. There are work-arounds, but it is an important enough problem to have been addressed by the standards committee, which are adding "move" semantics to the language. In the meantime, there are several possible work-arounds (all rather a bother to program): the easiest to implement, at least in a single threaded environment, is probably some form of copy on write. Alternatively, you use "helper objects" foo() doesn't do anything itself but return a helper object; an assignment operator is declared to accept the helper object, and the actual logic in foo is moved into the helper object. Something like: class Array { public: // ... template< typename Helper > Array& operator=( Helper const& h ) { h.getData( this ) ; // Modify data here... return *this ; } } ; This allows foo (or rather, the code which used to be in foo) to modify existing data, rather than having to create a new instance. A temporary array is created in foo(), this array is passed to operator= and there each element of foo is copied to B. However, it would be useful if operator= could notice if the object is temporary. Because then we could write if (temporary) { a = A.a; A.a = NULL; } And leak the memory which a used to point to:-). The classical idiom here is swap, so that the destructor of the temporary takes care of releasing any resources originally used by the target. -- James Kanze (GABI Software) email:ja*********@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 Oct 29 '07 #10

 P: n/a On Oct 29, 1:45 pm, Erik Wikström wrote: James Kanze wrote: The real question is why the OP wants to know. There's likely a solution to his real problem which doesn't require such knowledge. Hmm okay this is what I had in mind. Assume you have a class array class array { public: array(int N) : n(N) { if (n) a = new double[N]; else a = NULL; } ~array(void) { delete [] a; } array operator=(const array &A) { if (n != A.n) { delete [] a; a = new double [A.n]; } for (register int i=N-1; i >= 0; --i) a[i] = A.a[i]; return *this; } private double *a; int n; } Assume now we have the function array foo(void); which creates an enormous array Then the following operation array B; B = foo(); Creates a tremendous amount of overhead. A temporary array is created in foo(), this array is passed to operator= and there each element of foo is copied to B. However, it would be useful if operator= could notice if the object is temporary. Because then we could write if (temporary) { a = A.a; A.a = NULL; } When this happens, foo gets destroyed after operator=, but it is only a NULL pointer, the real date is still there and not delete with ~array(void); Make A::operator= take the argument by Value instead of reference, then swap 'this' and the argument: So you remove one copy and create a new. By taking the argument by value a copy must be made so nothing is won. It is the asignment operator, so, yes, you need to do at least one copy. Consider the canonical exception safe operator=: A& A::operator=(A const& rhs) { A tmp(rhs); using std::swap; swap(*this, rhs); return *this; } You have to do a copy anyway, but if you capture by value instead of reference, you save a copy if the argument was an rvalue anyway. > The solution to the problem is called R-value References or Move Semantics and will be part of the next version of the standard. Using this you will be able to write move-constructors which transfers ownership from the temporary to the new object. Sure, R-Value references are a wellcome addition. But a take-by-value operator= can implemented *now* without language changes. And anyway, if you are willing to do some scafold work, move semantics are implementable in C++03 without language extensions, see the works of Alexandrescu and Abrahams. (the take take-by-value operator= is an useful subset of that). -- Giovanni P. Deretta Oct 29 '07 #11

 P: n/a On Oct 29, 3:18 pm, Erik Wikström class Foo {}; Foo bar(int i) { Foo f1, f2; if (i == 1) return f1; return f2; } int main() { Foo f; f = bar(1); } In this case I can not make my compiler omit the copy-construction. It is because of situations like this (among other things) that move semantics were added. If operator= has signature Foo& Foo::operator=(const Foo&) you need to do two copies of Foo: one at return time (when you copy f1 or f2 to the return temporary) and one inside operator=. If you take by value, the temporary becomes directly the argument of the operator= and you can swap it with 'this' saving a copy. Whether bar if NRV optimizable or not has nothing to do with the ability of the compiler to omit the temporary used to copy- construct a named object (the argument to operator=). They are two distinct optimizations, and, in your example, each can save a copy. Also, while NRVO is not always applicable (as you have shown), the latter optimization can be done pratically always (and AFAIK pratically all compilers do it). BTW, if you rely on those two optimizations, you can force a move with this simple function: template T to_rvalue(T& x) { T result; using std::swap; swap(result, x); return result; } ... some_type x; some_type y = to_rvalue(x); // move some_type z; z = to_rvalue(y); // move to_rvalue assumes that T is default constructible and swappable. Also it will only useful is swap can be implemented efficiently (which is true for all movable objects) -- Giovanni P. Deretta Oct 29 '07 #12

 P: n/a On Oct 29, 4:22 pm, gpderetta

 P: n/a On Oct 29, 6:13 pm, James Kanze = 0; --i) a[i] = A.a[i]; return *this; } private double *a; int n; } Assume now we have the function array foo(void); which creates an enormous array Then the following operation array B; B = foo(); Creates a tremendous amount of overhead. True. There are work-arounds, but it is an important enough problem to have been addressed by the standards committee, which are adding "move" semantics to the language. In the meantime, there are several possible work-arounds (all rather a bother to program): the easiest to implement, at least in a single threaded environment, is probably some form of copy on write. Alternatively, you use "helper objects" foo() doesn't do anything itself but return a helper object; an assignment operator is declared to accept the helper object, and the actual logic in foo is moved into the helper object. Something like: class Array { public: // ... template< typename Helper > Array& operator=( Helper const& h ) { h.getData( this ) ; // Modify data here... return *this ; } } ; This allows foo (or rather, the code which used to be in foo) to modify existing data, rather than having to create a new instance. A temporary array is created in foo(), this array is passed to operator= and there each element of foo is copied to B. However, it would be useful if operator= could notice if the object is temporary. Because then we could write if (temporary) { a = A.a; A.a = NULL; } And leak the memory which a used to point to:-). The classical idiom here is swap, so that the destructor of the temporary takes care of releasing any resources originally used by the target. 1). I think the proposed syntax for rvalue ref (type&&) is inefficient in that you can not specify the '*this' object of an instance- member_function as temporary.I mean you can not specialize member functions based on r/l-valueness of the object the way you do for const/volatility.Plz see this: http://groups.google.com/group/comp....dae8f5a0eb863f 2). Let us assume that special syntax is provided for r-value refrences .take the following: #define lvalue(type) type& #define rvalue(type) /*equivalent to rvalue ref syntax*/ class foo{ public: foo(); foo (const lvalue(foo) ); foo (const rvalue(foo) ); }; foo bar(){ foo result;//bar always returns result. /////... return result; }; class ret_type some_fn(params){ foo obj(bar()); ////... } If RVO/NRVO is switched of a call to 'some_fn' will result to the following sequence: call bar default construct bar::result ... copy construct temporary from bar::result destruct result move construct some_fn::obj from temporay destruct temporary .... as you can see the unnecessary copy from bar::result to temporary and destruction of result can not be removed via rvalue ref.One can replace the extra copy with move but the extra construction and destruction is not removed: foo bar(){ foo result;//bar always returns result. ... return (rvalue(foo))result;//cast to rvalue }; the senario changes to: call bar default construct bar::result ... move construct temporary from bar::result destruct result move construct some_fn::obj from temporay destruct temporary .... but the contruction of temporary from bar::result and destruction of result still perform.IMHO it is quite simple to calcuulate the address of temporary at the beginning of bar ,and since bar is always returning the same object ,the compiler must 'union' the temporary with 'result' and avoid constructing the temporary from result .IMHO this special case should become part of standard behavoir rather than an optimization choice.Another option would be: http://groups.google.com/group/comp....52daa8ac2e51f1 regards, FM. Oct 30 '07 #14

 P: n/a On Oct 30, 9:26 am, James Kanze There are doubtlessly other solutions, based on helper classes, which can also be used. They are significantly more complex, but can be made to avoid the copy even if the compiler doesn't implement NRVO or RVO. Yes, you can implement move sematics in C++03, but it is a bit involved, and where would you need them the most (inside std containers), they can't be used (unless you reimplement the containers of course). Is there any relatively modern compiler which doesn't do NRVO or RVO (in most reasonable cases at least) or doesn't eliminate temporaries used in copy initialization? -- Giovanni P. Deretta Oct 30 '07 #15

 P: n/a On 2007-10-30 06:15:18 -0400, terminator 1). I think the proposed syntax for rvalue ref (type&&) is inefficient in that you can not specify the '*this' object of an instance- member_function as temporary.I mean you can not specialize member functions based on r/l-valueness of the object the way you do for const/volatility. You can't use that notation to overload on the qualifiers for the object, because it applies to ordinary arguments. But as of the latest draft C++0x standard (N2461), you can use & or && to overload member functions in the same way you use const or volatile. -- Pete Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The Standard C++ Library Extensions: a Tutorial and Reference (www.petebecker.com/tr1book) Oct 30 '07 #16

 P: n/a On Oct 30, 4:36 pm, Pete Becker

 P: n/a Hi all, Thanks for all your extensive replies. As I see it, it is a rather well known problem of all the copying and it shows that there is not really a direct solution in standard c++. I have implemented a swap option in it, which basically is the fastest way for my problem. Thanks for your help all Klaas Oct 31 '07 #18

### This discussion thread is closed

Replies have been disabled for this discussion. 