FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
C++
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
June 01, 2006

Living By the Rules: Part II

(Page 2 of 6)

Making Common Mistakes Legal

It's frustrating to have to add spaces between ">" symbols when you're wrapping up a declaration that involves nested templates. Many people's natural instinct is to jam them all together:

std::vector<std::pair<int, std::string>> vect;

Today, that's illegal: The two ">" symbols at the end are treated as a shift-right, and the declaration is ill-formed. In the next version of the Standard, that will be legal. As long as you're unwinding template IDs, a non-nested ">>" is treated as two ">"s and not as a single token. This means that you have to be a little careful if you really want to have a shift-right somewhere in your template usage, but that's far less common. For example, the working draft of the new Standard gives this example of code that will be invalid:

template <int i> class X { /* ... */ }; template <class Ty> class Y { /* ... */ }; Y<X<6>>1>> x4;

To make it valid, you have to put parentheses around the arithmetic expression:

Y<X<(6>>1)>> x4;

Another common problem occurs when template code uses a type name inside a friend declaration. Technically, the type name has to be what the Standard calls an "elaborated type specifier." That makes this usage illegal:

template <class Ty> class no_children
{   // template to block inheritance from Ty
no_children() {}
friend class Ty;
};

class final : virtual no_children<final> { /* ... */ };

If it worked, this pattern would prevent deriving from the class final [4]. There's a problem, though: In the declaration friend class Ty, the name Ty is not an elaborated type specifier, so it can't be the target of a friend declaration. That's been changed, so that a broader range of syntactic elements can be used as the target of a friend declaration, and this code will be legal.

A common beginner's mistake is writing something like this:

struct S { S() : i(0), j(0) {} S(int j0) { S(); // mistake j = j0; } int i, j; };

The mistake is thinking that the line marked "mistake" applies the default constructor to the object being constructed, thus setting i and j to 0. Of course, we've all learned to patiently explain that what that line actually does is create an unnamed temporary object of type S, and immediately destroy it. That will still be a mistake, but there is a new syntax that allows a constructor to delegate part or all of its work to another constructor:

struct S { S() : i(0), j(0) {} S(int j0) : S() // changed { j = j0; } int i, j; };

The line marked "changed" has a constructor as its initializer list. That tells the compiler to apply that constructor first, and then to execute the body of the constructor being defined [5].

Previous Page | 1 Living By the Rules: Part II | 2 Making Common Mistakes Legal | 3 Handy Extensions | 4 Library Interface Improvements | 5 C99 Compatibility | 6 Conclusion Next Page
TOP 5 ARTICLES
No Top Articles.



MICROSITES
FEATURED TOPIC

ADDITIONAL TOPICS

INFO-LINK