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, Ive been writing online for years. But whats new is that Im about to start editing and managing other peoples writing online as well.
Since late last year, MSDNs 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 Ive never before held anywhere.
Today the intersection between my portal and CUJs 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 wont 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, Im 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, Ill 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 Stroustrups 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 arent allowed, could you give a short answer why, especially since Stroustrups 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 cant have a partial specialization of function templates, and thats 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. Theres a big difference between those two terms. In that sense, Bjarne and I are not in conflict.
But that doesnt mean hes right. What Bjarne calls a specialization isnt a specialization, partial or otherwise. Its 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, ambiguousIn the more complex example from Bjarnes 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.
Ill 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 dont 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 cant, 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:
- It relies on behavior outside the Standards province. Once an objects lifetime ends, the Standard-required properties and behavior no longer apply [5].
- delete p has the side effect of calling operator delete to free *ps memory. By the time you check p->deleted_, you dont necessarily know that *ps memory has not already been invalidated by your operating system, returned to the free store, reused by another object, or otherwise made unavailable.
- Because you dont know that ~X was called via delete, the deleted_ flag may show a false positive.
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:
- It falls within the language rules.
- It does not access an objects state after the object is deleted.
- It senses deletion in operator delete instead of ~X, reducing the chance of false positives.
As written, the program contains a few simple test cases. Those tests show that the program cant discriminate between
delete p;and
p->~X(); p->operator delete(p);Ill 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 didnt specify a language, my answer applies to both C and C++.
If Im reading you correctly, youre wondering when or even if its 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, Id 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:
- Maximize maintainability. Versions 2 and 3 favor maintainability, by declaring zed in one easily discoverable place. Version 1 is less maintainable, especially if zed is called in multiple functions and thus must be redundantly declared.
- Maximize encapsulation. Version 3 favors encapsulation, by textually hiding the zed declaration in a separate header file. Version 1 favors encapsulation in a different way, by confining zeds declaration to the smallest possible scope.
- Minimize name collisions. By declaring zed global, Versions 1 and 2 allow other parts of the file to inadvertently call zed. They also prevent a global object or (in C) another global function named zed from being declared.
Overall, I favor Version 3 over Version 2 pretty universally, so the real competition is between Versions 1 and 3.
The only time Id recommend Version 1 is when you have high certainty the external function will be used in exactly one place and wont 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 its reasonable to forward-declare one of the functions locally. Putting the zed declaration into a header (as in Version 3) doesnt make much sense, as it cant 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 wont 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 classs constructors.
Is this normal behavior? Ive included a small example that demonstrates it. (And yes, Im 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.
Im 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 classs copy constructor is callable with a single argument convertible to the classs (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 dont 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 BAs 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 dont want your B constructor to have a default argument, youll have to call it with two arguments:
B b2(b1, false);In that case, youll want to ferret out places youre inadvertently using the generated B copy constructor. To do that, simply declare your own copy constructor as private in B, but dont 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.
[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.
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.