My question (which will follow after that, sorry for the long introduction, the question is in bold there ) was originally inspired by paragraph 23 in Herb Sutters Exceptional C ++ where we find something like this:
<notch>
... int main() { GenericTableAlgorithm a( "Customer", MyWorker() ); a.Process(); }
from
class GenericTableAlgorithm { public: GenericTableAlgorithm( const string& table, GTAClient& worker ); bool Process(); private: struct GenericTableAlgorithmImpl* pimpl_;
class GenericTableAlgorithm { public: GenericTableAlgorithm( const string& table, GTAClient& worker ); bool Process(); private: struct GenericTableAlgorithmImpl* pimpl_;
</ notch>
Now I have the following problems with this code (and no, I in no way doubt Mr. Satter's skill as an expert in C ++):
- An example like this will not work, since GTAClient & worker is a non-const reference that cannot accept temporary ones, but maybe it would be written in advance or standardly, whatever that is.
- Which makes me wonder what he is going to do with reference to the employee, even if problem 1. is ignored.
Obviously, the intention is to use MyWorker in the NVI GenericTableAlgorithm , accessed through the GTAClient (polymorphic) interface; this excludes that the implementation has a member (value) of type GTAClient , as this will lead to cutting, etc. Value semantics are not well combined with polymorphism.
It cannot have a data item of type MyWorker , since this class is unknown to GenericTableAlgorithm .
Therefore, I conclude that it should have been used with a pointer or link, while preserving the original object and the polymorphic character. - Since pointers to temporary objects (
MyWorker() ) are rarely a good idea, I assume that the author’s plan was to use the extended lifetime of the temporary bindings on (const) links and store such a link in the pimpl_ object points to and uses it from there. ( Note: GTAClient also lacks the clone-member function that could do this work, and don’t assume that the RTMA type factory info is hiding in the background. )
And here (finally!) My question is asked in: (How) can pass a temporary value for an element of an element of a class with an extended service life legally?
The standard in §12.2.5 (version C ++ 0x, but the same in C ++, does not know the chapter number) makes the following exception to the extension of life: "- temporary binding to the reference member in the ctor-initializer constructors (12.6.2 ) is saved until the constructor exits. "
Therefore, the object cannot be used in calling client code a.Process (); because the indicated temporary number from MyWorker() already dead!
Consider now an example of my own development that demonstrates the problem (tested on GCC4.2):
#include <iostream> using std::cout; using std::endl; struct oogie { ~oogie() { cout << "~oogie():" << this << ":" << m_i << endl; } oogie(int i_) : m_i(i_) { cout << "oogie():" << this << ":" << m_i << endl; } void call() const { cout << "call(): " << this << ":" << m_i << endl; } int m_i; }; oogie func(int i_=100) { return oogie(i_); } struct kangoo { kangoo(const oogie& o_) : m_o(o_) { } const oogie& m_o; }; int main(int c_, char ** v_) { //works as intended const oogie& ref = func(400); //kablewy machine kangoo s(func(1000)); cout << ref.m_i << endl; //kangoo referenced oogie is already gone cout << s.m_o.m_i << endl; //OK, ref still alive ref.call(); //call on invalid object s.m_o.call(); return 0; }
which produces the output
oogie (): 0x7fff5fbff780: 400
oogie (): 0x7fff5fbff770: 1000
~ oogie (): 0x7fff5fbff770: 1000
400
1000
call (): 0x7fff5fbff780: 400
call (): 0x7fff5fbff770: 1000
~ oogie (): 0x7fff5fbff780: 400
You can see that in the case of const oogie & ref , the temporary return value of the func () function associated with the link has the extended lifetime of the referenced link (to the end of the main link), so this is normal.
BUT: The 1000-oogie object is already destroyed right after the creation of kangoo-s. The code works , but we are dealing with an undead object here ...
To ask a question again:
First, did I miss something, and is the code correct / legal? .
Secondly, why doesn't GCC give me any warnings about this, even if -Wall is specified? Should he? Could it be?
Thank you for your time,
Martin