
The summer 2010 ISO C++ meeting was held on August 2-7 in Rapperswil, Switzerland. The post-meeting mailing is now live, including meeting minutes and other information.
In March (trip report), we voted the last set of feature changes into a Final Committee Draft (FCD) and, after two weeks of scurrying to apply the changes, the result was balloted among ISO national bodies from Mar 27 through Jul 27. That means we received the ballot results and ~250 pages of comments just in time for the start of this meeting, and have busied ourselves with starting to process them.
Where are we in the process?
For this and the next couple of meetings (Batavia IL in November, and Madrid in March), the committee is doing ballot resolution and dealing with national body comments. Because we issued a Final Committee Draft, ISO C++0x can officially no longer add new features. All that we can do is address national body comments, and make any other bug fixes we find.
The good news is that comment volume and content is considerably lighter than the comments on the CD ballot, and we expect to be able to finish handling all of them in two more meetings and, barring any surprises, vote on the Final Draft International Standard (FDIS) for C++0x standard after the Madrid meeting in March. If that happens and that ballot succeeds, the C++0x standard will be published in 2011 and we can start truthfully calling it C++11 and stop pretending about the ‘0’.
Low probability contingency: If between now and Madrid something unforeseen arises and we feel the standard is not in good enough shape to ship and needs more work, the fallback route would be to instead issue a second Final Committee Draft (FCD), which adds another comment cycle and another year to the overall process. We hope that won’t be necessary.
What did we do at this meeting?
In Rapperswil, the Core, Library, and Concurrency subgroups spent the week triaging the comments and beginning to resolve the biggest or most potentially controversial questions first.
We accomplished a lot. Here are the key decisions, some of them tentative:
Attributes: alignment, noreturn, and virtual control. My biggest (but not only) beef with C++0x [[attributes]] is that the standard itself was abusing attributes as disguised [[keywords]]. Attributes are acceptable as pure annotations, but they should have no semantic effect on the program. The problem was that some C++0x attributes did have such an effect, notably the virtual control attributes, so I’ll start with those.
My personal hot button for this meeting was that C++0x not look like the following example, which is taken almost verbatim out of the FCD ballot draft. The semantics are excellent and desirable, but the syntax is clearly not what anyone would design in a fresh programming language:
class [[base_check]] Derived : public Base { public: virtual void f [[override]] (); virtual double g [[final]] ( int ); virtual void h [[hiding]] (); };
The road to the above was paved with good intentions. But it’s a clear example of adding language keywords dressed in attributes’ clothing, and not something I want to be responsible for forcing three million C++ developers to live with until the end of time.
Fortunately, the committee has tentatively agreed and decided to pursue replacing these attributes with keywords. For the next meeting, I and several other experts volunteered to write a concrete change proposal paper on this, and will include a description of two categories of options:
- fully reserved words, which can break existing user code that uses the words as variable or type names and so would mean we need to pick uglier names; and
- contextual keywords as done in C++/CLI, which does not break existing user code and so lets us pick the ideal nice names, but which would be the first contextual keywords in ISO C++ (and there’s always resistance to being the first).
This week’s straw poll sentiment favored fully reserved keywords over contextual keywords, but both had stronger support than attributes.
The other two kinds of attributes that we considered changing to keywords were [[align]] to specify the alignment of a type, and [[noreturn]] to specify that a function never return. We decided to pursue changing [[align]] to a keyword, and leaving [[noreturn]] alone.
noexcept, part 1: Destructors noexcept by default. This is a tentative resolution to be written up and voted on at the next or following meeting. To me, this is an important achievement, because we have always taught people never to throw from destructors (see Exceptional C++ Item 16, subtitled “Destructors That Throw and Why They’re Evil”) and now the language will enforce that at least by default.
Background: Why are throwing destructor bad? Because any type with a destructor that could throw is inherently unsafe to use nearly all the time. Any type whose destructor could throw is already banned from use with the standard library (i.e., can’t be put in a container or passed to an algorithm), cannot be reliably used as a class data member (because if while constructing the containing object another class data member throws an exception, and then as the destructors of the already-constructed members are called this member’s destructor throws an exception, the can’t-have-two-active-exceptions rule kicks in and the program terminates immediately), and cannot even be reliably used as an array (because if while constructing the array one constructor throws an exception, and then as we call the destructors of the already-constructed objects one of those throws an exception, the can’t-have-two-active-exceptions rule kicks in and the program terminates immediately).
We acknowledged that this change will break some code by making it call terminate. We (not quite unanimously) agreed that such code not only deserves to be broken, but in fact is already broken in a less obvious way; for some examples, see GotW #47. Granted, we know that a small but steady trickle of developers have regularly tried to be clever, claiming they have legitimate uses for a throwing destructor. I’ve seen a number of such claims, and they’ve always turned out to be bad, unsafe code. This change will do the world a favor by making such code terminate deterministically at test time the first time you try to execute it.
If someone really truly feels they are an exception, that’s fine, they are allowed to write “noexcept(false)” on their destructor. Personally, though, I’d recommend that companies consider automatically rejecting attempts to check in any C++ source file containing that string.
no except, part 2: noexcept will be applied to the standard library. This also is important. At our previous meeting, we voted noexcept into the language (and deprecated exception specifications) but didn’t yet have time to apply noexcept to the standard library (which was still using exception specifications, and it’s odd for the standard to keep using a deprecated feature). That will now be done.
First, all empty exception specifications (which is all of them), currently spelled “throw()”, will be replaced with “noexcept”.
Second, all standard library functions whose description include “Throws: Nothing” will be changed to “noexcept”. This is an even bigger improvement, because it changes normative documentation/commentary into actual normative code.
Third, an analysis will be done of all move constructors used in the standard library, and we’ll see if all of those can also be changed to “noexcept”. That analysis will be reviewed at a future meeting and a course of action on this decided there.
noexcept, part 3: terminate stays. It was reaffirmed that if you violate a noexcept specification, your program will call std::terminate and not continue execution in what would be a corrupt state. Stack unwinding is not required.
Looking forward
Finally, here are the scheduled dates and locations for the next few ISO C++ standards committee meetings:
- November 8-13, 2010: Batavia, Illinois, USA
- March 21-26, 2011: Madrid, Spain
Herb
Why don’t you call it c++0xb instead? That way you don’t even have to drop the 0x prefix!
Thanks for keep us up to date, very interesting article!
One problem I have with that is that I don’t know what to do with a partially completed destructor. Once we enter the destructor, the object’s lifetime is over, and any further reference to it can cause real problems. Therefore, if we leave a destructor with an exception, we potentially have a real zombie object. We can deallocate the memory, but if it had any resources (i.e., it needs a nontrivial destructor), we’ve got potential leaks, and the principles of RAII are violated. (This could be fixed by resume-on-exception; see Stroustrup’s Design & Evolution book, section 16.6, as to why resumption is not in C++.)
Presumably, also, if your output stream finds the disk full, it wants to do something like pop up an alert or uninstall your competitor’s software or something else to gain more space, and try again. If it’s in a destructor, it can’t try again, because the object doesn’t really exist. Either it handles a problem inside the destructor (in which case it doesn’t throw an exception it doesn’t catch), or the data is lost. This is true no matter whether you propagate both exceptions or only the original.
In fact, if you propagate only the original exception, what’s the use of the one thrown in the destructor? It isn’t going to be caught and processed anywhere. All it will do is not execute part of the destructor, which itself is a Bad Thing. The only logical course of action is to propagate both exceptions, and presumably therefore an arbitrary number of exceptions. (Calling terminate() if there are two exceptions is an understandable rule. Raising the number will only confuse things.)
This means that an exception handler can’t be in a known state. There could be any number of other things that went wrong that invalidate the assumptions the handler operates under, and the only way to know would be to examine other exceptions, leading to massive coupling among error handlers.
I believe these are philosophical issues, not technical. What should happen when a destructor throws, and what will be the effect on writing C++?
Besides all kinds of technical issues, I have never seen anyone give a sound logical reason why you should not throw an exception during destruction.
Suppose you’re flushing data to a file in the dtor. If this doesn’t work ( f.e. disk full ) then you cannot use an exception to signal this.
The can’t-have-two-active-exceptions rule is purely technical. When unwinding due to an exception you could decide to not allow any more exceptions. Only the original exception is allowed.
I bet there a number of other technical issue with this proposition, it’s just that everyone seems to have decided that exceptions during dtors is philosophically bad, while the reasons for it are technical.
C++ can’t deal with it, for the simple reason that C++ won’t deal with it. This is not to be applauded, it’s just a fact of life.
I have a real hard time imagining why anyone would want to throw an exception from a destructor. I think there’s some failure to think this through…
If your destructor fails, there’s no way to recover, as whatever object you are cleaning up has gone out of scope already.
“Destructors noexcept by default” -> this is great! But why do we need noexcept(false) for destructors at all?
The big problem I have with context-dependent constructors is that they’re really messy. By definition, you’re duplicating functionality, so you have to factor out the common elements into a function, and make sure both destructors and function are maintained properly. Testing becomes harder, since there’s two ways to destroy an object.
Given sufficient discipline in the programming shop, this could work. In most of the places I’ve worked, this would be of little or no value and just asking for trouble.
And, of course, it’s one more feature in a language with tons of them to remember.
Therefore, I’m very happy that C++ doesn’t do this.
@crusader
At the least, the less safe dtor should have the new, uglier syntax.
@JK
http://www.gotw.ca/gotw/047.htm
I wholeheartedly agree with removing the [[attribute]] and replacing it with keywords, but I’m probably less keyword-averse than most.
Speaking of which, I’d love to see “this_t” (type of the this pointer) and “return_t” (type of the current function’s return value) as omnipresent typedefs. decltype(this) could do for the first, I suppose, but I’m not aware of a good way to get the latter. Maybe in C++0xB TR1?
I think reference counted Stream is clasical example where you have to throw exception in dtor. So for this is std::uncaught_exception only way. Of course you can forget this, but then you are back in explicit tracking who is owner, which I view as much more error prone then uncaught_exception.
I suppose I should mention if there’s any resistance to that idea, that the standard could reserve some prefix as introducing new keywords. Thus, if “cpp_” were that prefix, then new keywords can be formed such as, “cpp_base_check,” “cpp_align,” etc.
The standard already reserves identifiers double underscores for the implementation. Why not take new keywords from that namespace? It should be pretty easy to find common ground among the implementers for sane keywords. Thus, __base_check, __align, etc. could be used if implementers agree they don’t conflict or they are willing to code around them.
@crusader:
Could you check std::uncaught_exception?
Enforcing nothrow dtors is good… almost always. But I sometimes would really like to allow dtor to throw if stack is not being unwound — obviously this kind of object requires special treatment, but it is better to have this possibility than not.
I wish C++ had ability to distinguish between dtor used during exception-triggered stack unwinding (that disallowed to throw) and “normal” dtor (that can throw), i.e. smth like:
class Foo
{
~Foo(); // normal dtor
~~Foo(); // dtor used during exception-generated unwinding
};
if ~~Foo() is absent — it defaults to ~Foo()…
But I guess it is too late for that.
Another vote for adding new keywords, even if it means breaking old code. “override” is one example; I use it in Visual C++ and it’s great.
I wish the same rationale was applied to C++0x keywords like “enum class” and “long long”. I’d much rather they used new keywords, instead of mixing old ones together. It will be very difficult to search code for “enum” without finding “enum class” or “long” without finding “long long”.
I think it will be pretty nice to have base_check and override as keywords.
I hope they don’t try to come up with a strange keyword replacement to avoid breaking old code…
Porting to a new version of a compiler often requires tweaks to the code base, whether there’s new keywords or not. Expecting old code to run unchanged on new compilers is unrealistic. I’d rather see sane keywords that require minor code changes, than crazy keywords.
So it will be C++0xB?
Hi, thanks for keeping us updated.
Has someone thougt about a c-v-qualifier(const-expression) like noexcept(const-expression) ?
So that the following code will work
class Foo
{
int x = 0;
public:
template
const(t_const) volatile(t_volatile) int &
get() const(t_const) volatile(t_volatile)
{
return x;
}
};
int main()
{
Foo f1;
const volatile f2;
f1.get() = f2.get();
}
Daniel
Thanks for the update, Herb!
I’m glad to hear the horrible attribute syntax for base_ckeck and alignment goes away. Maybe it’s a good idea to revive an older idea I saw somewhere (dunno where):
explicit class foo {
…
};
This syntax could prevent the generation of otherwise implicitly generated special functions and also force explicit hiding and overriding.