上一篇日志答案(c++)
2008-01-02 14:18:16
原文地址:
http://herbsutter.spaces.live.com/Blog/cns!2D4327CC297151BB!378.entry
作者是 herb sutter,不用怀疑权威性吧,哈~,Secretary of the ISO/ANSI C++ Standards Committee
-------------------正文开始--------------------------------------------------
A friend recently asked me whether Example 1 below is legal, and ifso what it means. It led to a nice discussion I thought I'd post here.Since it was in close to GotWstyle already, I thought I'd do another honorary one after all theseyears... no, I have not made a New Year's Resolution to resume writingregular GotWs. :-)
JG Questions
Q1: Is the following code legal C++?
// Example 1
string f() { return "abc"; }
void g() {
const string& s = f();
cout << s << endl; // can we still use the "temporary" object?
}
A1: Yes.
This is a C++ feature… the code is valid and does exactly what it appears to do.
Normally,a temporary object lasts only until the end of the full expression inwhich it appears. However, C++ deliberately specifies that binding atemporary object to a reference to const lengthens thelifetime of the temporary to the lifetime of the reference itself, andthus avoids what would otherwise be a common dangling-reference error.In the example above, the temporary returned by f() lives until theclosing curly brace.
Does this work in practice? Yes, it works on all compilers I tried (except Digital Mars, so I just sent a bug report to Walter to rattle his cage :-) ).
Q2: What if we take out the const... is Example 2 still legal C++?
// Example 2
string f() { return "abc"; }
void g() {
string& s = f(); // still legal?
cout << s << endl;
}
A2: No.
The"const" is important. The first line is an error and the code won'tcompile portably with this reference to non-const, because f() returnsa temporary object (i.e., rvalue) and only lvalues can be bound toreferences to non-const.
Note: Visual C++ does allow Example 2but emits a "nonstandard extension used" warning by default. Aconforming C++ compiler can always allow otherwise-illegal C++ code tocompile and give it some meaning -- hey, it could choose to allowinline COBOL if some kooky compiler writer was willing to implementthat extension, maybe after a few too many Tequilas. For some kinds ofextensions the C++ standard requires that the compiler at least emitsome diagnostic to say that the code isn't valid ISO C++, as thiscompiler does.
I once heard Andrei Alexandrescu give a talk on ScopeGuard where he used this C++ feature and called it "the most important constI ever wrote." And this brings us to the Guru Question, whichhighlights the additional subtlety that Andrei's code deftlyleveraged...
Guru Question
Q3: When the reference goes out of scope, which destructor gets called?
A3: The same destructor that would be called for the temporary object. It's just being delayed.
Corollary: You can take a const Base& to a Derived temporary and it will be destroyed without virtual dispatch on the destructor call.
This is nifty. Consider the following code:
// Example 3
Derived factory(); // construct a Derived object
void g() {
const Base& b = factory(); // calls Derived::Derived here
// … use b …
} // calls Derived::~Derived directly here -- not Base::~Base + virtual dispatch!
Doesthis work in practice on real compilers? Yes: Every compiler I haveaccess to calls the correct Derived destructor, including even ancientBorland 5.5 and Visual C++ 6.0 (and Digital Mars, though DM calls thedestructor at the wrong time, as noted above).
Andrei leveragesthis subtlety (of course) in his ScopeGuard implementation to avoidmaking the implementation classes' destructors virtual at all, which isokay in that case because those classes otherwise have no need for one.