Should this C ++ time reference to the reference element be illegal?

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_; //implementation }; class GTAClient { ///... virtual bool ProcessRow( const PrimaryKey& ) =0; //... }; class MyWorker : public GTAClient { // ... override Filter() and ProcessRow() to // implement a specific operation ... };
class GenericTableAlgorithm { public: GenericTableAlgorithm( const string& table, GTAClient& worker ); bool Process(); private: struct GenericTableAlgorithmImpl* pimpl_; //implementation }; class GTAClient { ///... virtual bool ProcessRow( const PrimaryKey& ) =0; //... }; class MyWorker : public GTAClient { // ... override Filter() and ProcessRow() to // implement a specific operation ... }; 

</ 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

+6
c ++ temporary object-lifetime
source share
4 answers

I think this is a difficult part, which is not too clear. There was a similar question just a couple of days ago.

By default, temporary files are destroyed in the reverse order of construction when the full expression in which they were created is completed. Everything is fine and understandable here, but then exceptions arise (12.2 [class.temporary] / 4,5), and everything becomes confusing.

Instead of dealing with the exact wording and definitions in the standard, I approach the problem from an engineering / compiler point of view. Time series are created on the stack, when the function ends, the stack frame is freed (the stack pointer returns to its original position before the function starts).

This means that a temporary person can never survive the function in which it was created. More precisely, he cannot survive in the area where he was defined, even if he really can survive the full expression in which he was created.

None of the exceptions in the standard falls out of this restriction; in all cases, the lifetime of the temporary is extended to a point that is guaranteed not to exceed the call to the function in which the temporary was created.

+4
source share

The original article "Guru of the week" is here: Guru of the week No. 15 .

Part of your answer may come from Herb himself, in “Candidate for the most important const” - part of “const” assigning a temporary link is really important.

So it looks like this is a mistake in the original article.

+3
source share

I have always believed that passing address parameters (whether it be a reference or a pointer) requires an understanding of the lifetime of the transferred thing. Period. The end of the discussion. It just seems to be an attribute of a non-garbage environment and not related to it.

This is one of the benefits of GC.

In C ++, sometimes life issues were resolved with:

X :: clone

or by explicit documentation of the interface, for example:

"until the execution of Y, to ensure that the instance of X passed in the parameter x is maintained throughout the lifetime of Y"

or

explicit copying of x into the gut of the receiver

It is just C ++. The fact that C ++ added a guarantee on const links was nice (and really some necessary), but it was.

Thus, I believe that the code shown in the question is incorrect, and I believe that it should have been:

 int main() { MyWorker w; GenericTableAlgorithm a( "Customer", w); a.Process(); } 
+1
source share

I do not think you can do this using current C ++. You need to move the semantics that will be introduced in C ++ x0.

0
source share

All Articles