C/C++ Contributing Editors


Uncaught Exceptions: The Great Panjandrum

Bobby Schmidt

Templates are hard enough to understand without all the inconsistent terminology being used in C++ literature. Bobby clears up an especially troublesome case.


Copyright © 2001 Robert H. Schmidt

In this space last month, I discussed my increasing preference for writing online. Indeed, between my work for CUJ and that for MSDN, I’ve been writing online for years. But what’s new is that I’m about to start editing and managing other people’s writing online as well.

Since late last year, MSDN’s had a small developer portal covering the nascent .NET Framework [1]. My mission this year is to turn that portal into a serious online publication and resource. For that, I will be the content strategist and Editor-in-Chief, two roles I’ve never before held anywhere.

Today the intersection between my portal and CUJ’s traditional domain is pretty small. I foresee that intersection growing significantly over the next couple of years. In particular, I expect that .NET will influence the role and evolution of C++ relative to other languages. I also won’t be surprised if the eventual ECMA standards for C# and the Common Language Infrastructure inform the next version of the ISO C++ Standard.

For now, I’m just trying to inject the site with some editorial presence, rationale, and personality. Once I get things really swinging, I invite you to come take a look and tell me what you think. If I can do half as good a job as Marc has done with CUJ in my nearly six years here, I’ll be beyond thrilled.

(All of this assumes I actually get to do what I want to. Microsoft is a big company, .NET is complicated business, and a lot can change. By the time you actually read this, we may have a completely different mission.)

Impartial Specialization

Q

Hi,

Thank you for your answer (in CUJ October 2000) to my question on partial specialization of templates.

You said that partial specializations of function templates are not allowed by the C++ Standard. Now I found the following passages in Stroustrup’s The C++ Programming Language (3rd edition). In section 18.6.8, Stroustrup discusses

template<class T>
void swap(T &a, T &b)
    {
    T tmp = a;
    a = b;
    b = tmp;
    }

as a template within <algorithm>. He also writes (in section 16.3.10) that when T has the form vector<X, A> one can improve it using the specialization (!)

template<class X, class A>
void swap(vector<X, A> &x,
        vector<X, A> &y)
   {
   x.swap(y);
   }

from <vector>.

Stroustrup says that this specialization is provided by the Standard. Does this contradict your statement that partial specializations of function templates are not allowed?

On the other hand, if partial specializations of function templates really aren’t allowed, could you give a short answer why, especially since Stroustrup’s swap example above proves their usefulness?

Thanks again! — Achim Kupferoth

A

While Bjarne does not contradict me, I do believe he is mistaken.

I wrote that you can’t have a partial specialization of function templates, and that’s true: while the C++ Standard is rife with mention of partial class-template specialization, it is utterly silent about partial function-template specialization.

In the passage you cite, Bjarne asserts that

template<class X, class A>
void swap(vector<X, A> &,
        vector<X, A> &)

is a “specialization,” not a “partial specialization.” There’s a big difference between those two terms. In that sense, Bjarne and I are not in conflict.

But that doesn’t mean he’s right. What Bjarne calls a specialization isn’t a specialization, partial or otherwise. It’s really an overload of

template<class T>
void swap(T &, T &)

Just as you can overload regular (non-template) functions, you can also overload function templates. In the simple example taken from the Standard [2]:

template<class T>
void f();

template<int I>
void f();

two distinct f overloads are declared. The overloads are distinguishable via explicit template arguments:

f<int>(); // OK, calls 1st overload
f<2>();   // OK, calls 2nd overload
f();      // error, ambiguous

In the more complex example from Bjarne’s book, there are two swap overloads:

template<class T>
swap(T &, T &)

template<class T, class A>
swap(vector<T, A> &, vector<T, A> &)

From these overloads, you can create genuine explicit specializations [3]. As an example,

template<>
void swap<int>(int &, int &)

explicitly specializes the first swap overload for class T = int.

Listing 1 shows a code snippet demonstrating the two overloads and this example specialization.

I’ll add that your confusion is quite understandable. Class templates have partial specializations; function templates have the related concepts of overloads and partial ordering rules. And both have specializations and instantiations [4].

For reasons I don’t know, the C++ committee kept the class-template and function-template concepts and terminology maddeningly similar yet distinct. Everyone I know gets lost in this tangle eventually. Indeed, I discussed your question with Dan Saks; we both had to tease through the Standard to persuade ourselves that my interpretation is right.

Deletion Detector

Q

Bobby,

Consider this code:

X *p = new X;
delete p;

How do I test to find out that the object pointed to by p has been deleted?

Best Regards — Yu Tang

A

You can’t, at least not directly and 100 percent reliably.

One simple apparent solution: maintaining a deletion flag within each X object. The flag is cleared upon object construction and set upon object destruction:

class X
    {
public:
    X() : deleted_(false)
        {
        }
    ~X()
        {
        deleted_ = true;
        }
    bool deleted_;
    };

X *p = new X;
// ...
delete p;
if (p->deleted_)
    // ...

Lamentably, this simple approach suffers three large problems:

Listing 2 shows another approach: maintaining a list of currently allocated X objects. When an object is allocated via operator new, its address goes on the list; when the object is later deallocated via operator delete, its address leaves the list. If an object does not appear on the list, it has presumably been deleted.

This program mitigates the previous three problems:

As written, the program contains a few simple test cases. Those tests show that the program can’t discriminate between

delete p;

and

p->~X();
p->operator delete(p);

I’ll let you decide if this is a bug or a feature.

If you have sufficiently intimate knowledge of your compiler, you could possibly hack into its free-store manager to directly trap allocations and deallocations. You might even be able to probe the allocated blocks for system-specific flags indicating their state. In either event, I definitely leave such capers to you.

Local Heckle Decls

Q

Is it ever appropriate to place function prototypes locally within the functions that call them, rather than globally at the top of the file? If so, under what circumstances?

Thanks — Sharon Harvey

A

Since you didn’t specify a language, my answer applies to both C and C++.

If I’m reading you correctly, you’re wondering when — or even if — it’s better to write something like

/* Version 1 */

void f(void)
    {
    extern void zed(void);
    zed();
    }

instead of

/* Version 2 */

extern void zed(void);

void f(void)
    {
    zed();
    }

In the Real World, I’d argue that both of these versions are almost always inferior to

/* Version 3 */

/* zed.h */

extern void zed(void);

/* C or C++ source */

#include “zed.h”

void f(void)
    {
    zed();
    }

Your question brings into conflict three sacred design principles:

Overall, I favor Version 3 over Version 2 pretty universally, so the real competition is between Versions 1 and 3.

The only time I’d recommend Version 1 is when you have high certainty the external function will be used in exactly one place and won’t be changed — and even that recommendation is mild. Most programmers expect external declarations to appear at the top of a source file, either explicitly (Version 2) or implicitly (Version 3). Declaring them elsewhere would probably be a surprise.

The argument for Version 1 strengthens if the functions are static and reference one another:

static void f()
    {
    static void zed();
    zed();
    }

static void zed()
    {
    void f();
    }

In this case, I think it’s reasonable to forward-declare one of the functions locally. Putting the zed declaration into a header (as in Version 3) doesn’t make much sense, as it can’t be referenced in other translation units. And declaring it at the top of the file (as in Version 2) seems redundant, for the function would then be declared twice at global scope.

Finally, in the (admittedly suspect) case of

static void zed(void)
    {
    }

void f(void)
    {
    extern void zed();
    zed();
    }

you must declare the extern function locally in C++; otherwise the compiler would think you are multiply declaring the same zed at global scope with conflicting linkage specifiers.

Construction Malfunction

Q

Dear Bobby,

I work with Borland C++Builder v5.0 for Windows NT.

I decided that I needed to add an extra parameter to the copy constructor of a certain class (for reasons I won’t go into). I already had in the code a few places that I instantiated this class using its copy constructor, but I figured that since I did not set a default value for that parameter yet, the compiler would detect where I forgot to add that extra parameter.

After running the code, I was surprised to find out that there was such a place, and not only did the compiler not detect it, but when running it called the copy constructor of the base class without visiting any of the derived class’s constructors.

Is this normal behavior? I’ve included a small example that demonstrates it. (And yes, I’m aware that in this particular example using a virtual function to get the value is more correct.)

Thank you for your time. — Ido Grabinsky

A

I show a reduced version of your example as Listing 3.

In that example, you expect the line

B b2(b1);

to not compile; yet the line does compile (correctly), and the program does run (correctly). Chalk it up to C++ doing you an apparently unexpected and unwanted favor.

I’m sorry to break the news that

B(B const &, bool)

is most definitely not a copy constructor. According to the Standard [6]:

A non-template constructor for a class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments.

In short, a class’s copy constructor is callable with a single argument convertible to the class’s (possibly qualified) type. The constructor may have multiple parameters, so long as the extra parameters have default arguments.

Your constructor qualifies on the first point (the first parameter is of the correct type), but fails on the second (subsequent parameters don’t have default arguments).

Were you to write your constructor as

B(B const &, bool = false)

then the statement

B b2(b1);

would call through that constructor, and the run-time results would be

B
B

As written, B does actually have a copy constructor: the one provided implicitly by the language rules. What you interpret as a call to the base-class copy constructor is really a call to this derived-class copy constructor.

Because you did not define that copy constructor, the compiler generates a definition for you. (This is the “favor” I mentioned earlier.) That definition simply calls the A copy constructor, which in turn sets value to ‘A’ and leads to the behavior you see.

If you don’t want your B constructor to have a default argument, you’ll have to call it with two arguments:

B b2(b1, false);

In that case, you’ll want to ferret out places you’re inadvertently using the generated B copy constructor. To do that, simply declare your own copy constructor as private in B, but don’t implement it. All the calls through that copy constructor (outside B members) will be flagged by the compiler; you can then change them to reference your two-argument constructor instead.

Notes

[1] http://msdn.microsoft.com/net.

[2] Subclause 14.5.5.1/4.

[3] Explicit specialization of function templates is covered in Subclause 14.7.3.

[4] Adding to the trauma: the semantics of specializations and instantiations are intertwined, and the Standard is somewhat opaque about their distinction.

[5] Subclause 3.8/3.

[6] Subclause 12.8/2.

Although Bobby Schmidt makes most of his living as a writer and content strategist for the Microsoft Developer Network (MSDN), he runs only Apple Macintoshes at home. In previous career incarnations, Bobby has been a pool hall operator, radio DJ, private investigator, and astronomer. You may summon him on the Internet via BobbySchmidt@mac.com.