8

Why is the following code prints "xxY"? Shouldn't local variables live in the scope of whole function? Can I use such behavior or this will be changed in future C++ standard?

I thought that according to C++ Standard 3.3.2 "A name declared in a block is local to that block. Its potential scope begins at its point of declaration and ends at the end of its declarative region."

#include <iostream>
using namespace std;

class MyClass
{
public:
  MyClass( int ) { cout << "x" << endl; };
  ~MyClass() { cout << "x" << endl; };
};

int main(int argc,char* argv[])
{
  MyClass  (12345);
// changing it to the following will change the behavior
//MyClass m(12345);
  cout << "Y" << endl;

  return 0;
}

Based on the responses I can assume that MyClass(12345); is the expression (and scope). That is make sense. So I expect that the following code will print "xYx" always:

MyClass (12345), cout << "Y" << endl;

And it is allowed to make such replacement:

// this much strings with explicit scope
{
  boost::scoped_lock lock(my_mutex);
  int x = some_func(); // should be protected in multi-threaded program
} 
// mutex released here

//    

// I can replace with the following one string:
int x = boost::scoped_lock (my_mutex), some_func(); // still multi-thread safe
// mutex released here
4

4 回答 4

16

The object created in your

MyClass(12345);

is a temporary object which is only alive in that expression;

MyClass m(12345);

is an object which is alive for the entire block.

于 2009-09-07T10:40:18.473 回答
8

You're actually creating an object without keeping it in scope, so it is destroyed right after it is created. Hence the behavior you're experiencing.

You can't access the created object so why would the compiler keep it around?

于 2009-09-07T10:39:20.847 回答
5

To answer your other questions. The following is the invocation of the comma operator. It creates a MyClass temporary, which includes calling its constructor. It then evaluates the second expression cout << "Y" << endl which will print out the Y. It then, at the end of the full expression, will destroy the temporary, which will call its destructor. So your expectations were right.

MyClass (12345), cout << "Y" << endl;

For the following to work, you should add parentheses, because the comma has a predefined meaning in declarations. It would start declaring a function some_func returning an int and taking no parameters and would assign the scoped_lock object to x. Using parentheses, you say that the whole thing is a single comma operator expression instead.

int x = (boost::scoped_lock (my_mutex), some_func()); // still multi-thread safe

It should be noted that the following two lines are equivalent. The first does not create a temporary unnamed object using my_mutex as the constructor argument, but instead the parentheses around the name are redundant. Don't let the syntax confuse you.

boost::scoped_lock(my_mutex);
boost::scoped_lock my_mutex;

I've seen misuse of the terms scope and lifetime.

  • Scope is where you can refer to a name without qualifying its name. Names have scopes, and objects inherit the scope of the name used to define them (thus sometimes the Standard says "local object"). A temporary object has no scope, because it's got no name. Likewise, an object created by new has no scope. Scope is a compile time property. This term is frequently misused in the Standard, see this defect report, so it's quite confusing to find a real meaning.

  • Lifetime is a runtime property. It means when the object is set up and ready for use. For a class type object, the lifetime begins when the constructor ends execution, and it ends when the destructor begins execution. Lifetime is often confused with scope, although these two things are completely different.

    The lifetime of temporaries is precisely defined. Most of them end lifetime after evaluation of the full expression they are contained in (like, the comma operator of above, or an assignment expression). Temporaries can be bound to const references which will lengthen their lifetime. Objects being thrown in exceptions are temporaries too, and their lifetime ends when there is no handler for them anymore.

于 2009-09-07T15:54:38.017 回答
4

You quoted standard correctly. Let me emphasize:

A name declared in a block is local to that block. Its potential scope begins at its point of declaration and ends at the end of its declarative region.

You didn't declare any name, actually. Your line

MyClass (12345);

does not even contain a declaration! What it contains is an expression that creates an instance of MyClass, computes the expression (however, in this particular case there's nothing to compute), and casts its result to void, and destroys the objects created there.

A less confusing thing would sound like

call_a_function(MyClass(12345));

You saw it many times and know how it works, don't you?

于 2009-09-07T10:50:51.807 回答