Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guaranteed lifetime of temporary in C++?

Tags:

c++

Does C++ provide a guarantee for the lifetime of a temporary variable that is created within a function call but not used as a parameter? Here's an example class:

class StringBuffer { public:     StringBuffer(std::string & str) : m_str(str)     {         m_buffer.push_back(0);     }     ~StringBuffer()     {         m_str = &m_buffer[0];     }     char * Size(int maxlength)     {         m_buffer.resize(maxlength + 1, 0);         return &m_buffer[0];     } private:     std::string & m_str;     std::vector<char> m_buffer; }; 

And here's how you would use it:

// this is from a crusty old API that can't be changed void GetString(char * str, int maxlength);  std::string mystring; GetString(StringBuffer(mystring).Size(MAXLEN), MAXLEN); 

When will the destructor for the temporary StringBuffer object get called? Is it:

  • Before the call to GetString?
  • After GetString returns?
  • Compiler dependent?

I know that C++ guarantees that a local temporary variable will be valid as long as there's a reference to it - does this apply to parent objects when there's a reference to a member variable?

Thanks.

like image 852
Mark Ransom Avatar asked Feb 25 '09 05:02

Mark Ransom


2 Answers

The destructor for that sort of temporaries is called at the end of the full-expression. That's the most outer expression which is not part of any other expression. That is in your case after the function returns and the value is evaluated. So, it will work all nice.

It's in fact what makes expression templates work: They can keep hold references to that sort of temporaries in an expression like

e = a + b * c / d 

Because every temporary will last until the expression

x = y 

Is evaluated completely. It's quite concisely described in 12.2 Temporary objects in the Standard.

like image 56
Johannes Schaub - litb Avatar answered Oct 18 '22 18:10

Johannes Schaub - litb


litb's answer is accurate. The lifetime of the temporary object (also known as an rvalue) is tied to the expression and the destructor for the temporary object is called at the end of the full expression and when the destructor on StringBuffer is called, the destructor on m_buffer will also be called, but not the destructor on m_str since it is a reference.

Note that C++0x changes things just a little bit because it adds rvalue references and move semantics. Essentially by using an rvalue reference parameter (notated with &&) I can 'move' the rvalue into the function (instead of copying it) and the lifetime of the rvalue can be bound to the object it moves into, not the expression. There is a really good blog post from the MSVC team on that walks through this in great detail and I encourage folks to read it.

The pedagogical example for moving rvalue's is temporary strings and I'll show assignment in a constructor. If I have a class MyType that contains a string member variable, it can be initialized with an rvalue in the constructor like so:

class MyType{    const std::string m_name; public:    MyType(const std::string&& name):m_name(name){}; } 

This is nice because when I declare an instance of this class with a temporary object:

void foo(){     MyType instance("hello"); } 

what happens is that we avoid copying and destroying the temporary object and "hello" is placed directly inside the owning class instance's member variable. If the object is heavier weight than a 'string' then the extra copy and destructor call can be significant.

like image 20
Rick Avatar answered Oct 18 '22 18:10

Rick