Channels ▼
RSS

Befriending Templates


January 2003/Sutter's Mill


Sutter's Mill: Befriending Templates

by Herb Sutter


Let's say we have a function template that does SomethingPrivate() to the objects it operates on. In particular, consider the boost::checked_delete() function template, which deletes the object it's given — among other things, it invokes the object's destructor:

namespace boost {
  template<typename T> void checked_delete( T* x ) {
 // ... other stuff ...
    delete x;
  }
}

Now, say you want to use this function template with a class where the operation in question (here the destructor) happens to be private:

class Test {
  ~Test() { }               // private!
};

Test* t = new Test;
boost::checked_delete( t ); // ERROR:
  // Test's destructor is private,
  // so checked_delete can't call it.

The solution is simple: just make checked_delete() a friend of Test. (The only other option is to give up and make Test's destructor public.) What could be easier? And indeed, in the Standard C++ language there are two legal and easy ways to do it.

This article exists as a reality check: befriending a template in another namespace is easier said (in the Standard) than done (using real-world compilers that don't quite get the Standard right).

In sum, I have some good news, some bad news, and then some good news again:

  • The Good News: there are two perfectly good standards-conforming ways to do it, and the syntax is natural and unsurprising.
  • The Bad News: neither standard syntax works on all current compilers. Even some of the strongest and most-conformant compilers don't let you write one or both of the legal, sanctioned, standards-conforming, and low-cholesterol methods that you should be able to use.
  • The Good News (reprise): one of the perfectly good standards-conforming ways does work on every current compiler I tried except for gcc.

Let's investigate.

The Original Attempt

This article was prompted by a question on Usenet by Stephan Born, who wanted to do the above. His problem was that, when he tried to write the friend declaration to make a specialization of boost::checked_delete() a friend of his class Test, the code wouldn't work on his compiler (Microsoft Visual C++ 6.0).

Here's his original code:

// Example 1: One way to grant friendship
//
class Test {
  ~Test() { }
  <b>friend void boost::checked_delete( Test* x );</b>
};

Alas, not only does this code not work on the poster's compiler, it in fact fails on quite a few compilers. In brief, Example 1's friend declaration:

  • Is legal according to the Standard, but relies on a dark corner of the language.
  • Is rejected by many current compilers, including very good ones.
  • Is easily fixed to not rely on dark corners and work on all but one current compiler (gcc).

I am about to delve into explaining the four ways that the C++ language lets you declare friends. It's easy. I'm also going to have some fun showing you what real compilers do and finish with a guideline for how to write the most portable code.

Why It's Legal but Dark

When declaring friends, there are four options (enumerated in the C++ Standard, clause 14.5.3). They boil down to the following.

When you declare a friend without saying the keyword template anywhere:

  1. IF the name of the friend looks like the name of a template specialization with explicit template arguments (e.g., Name<SomeType>)
    • THEN the friend is the indicated specialization of that template.

  2. ELSE IF the name of the friend is qualified with a class or namespace name (e.g., Some::Name)
    AND that class or namespace contains a matching non-template function
    • THEN the friend is that function.

  3. ELSE IF the name of the friend is qualified with a class or namespace name (e.g., Some::Name)
    AND that class or namespace contains a matching function template (deducing appropriate template parameters)
    • THEN the friend is that function template specialization.

  4. ELSE the name must be unqualified and declare (or redeclare) an ordinary (non-template) function.

Clearly #2 and #4 only match non-templates, so to declare the template specialization as a friend we have two choices: write something that puts us into bucket #1, or write something that puts us into bucket #3. In our example, the options are:

// The original code, legal because it
// falls into bucket #3
//
friend void boost::checked_delete( Test* x );

or

// Adding "<Test>", legal because it
// falls into bucket #1
//
friend void boost::checked_delete<b><Test></b>( Test* x );

The first is shorthand for the second ... but only if the name is qualified (here by boost::), and there's no matching non-template function in the same indicated scope. Even though both are legal, the first makes use of a dark corner of the friend declaration rules that is sufficiently surprising to people — and to most current compilers! — that I will propose no fewer than three reasons to avoid using it.

Why To Avoid Bucket #3

There are several reasons to avoid bucket #3, even though it's technically legal:

1. Bucket #3 doesn't always work.

As noted above, it's a shorthand for explicitly naming the template arguments in angle brackets, but the shorthand works only if the name is qualified and the indicated class or namespace does not also contain a matching non-template function.

In particular, if the namespace has (or later gets) a matching non-template function, that would get chosen instead because the presence of a non-template function means bucket #2 preempts #3. Kind of subtle and surprising, isn't it? Kind of easy to mistake, isn't it? Let's avoid such subtleties.

2. Bucket #3 is a really edgy case, fragile and surprising to most people reading your code.

For example, consider this very slight variant — all that I've changed is to remove the qualification boost::.

// Variant: Make the name unqualified,
// and it means something very different
//
class Test {
  ~Test() { }
  friend void checked_delete( Test* x );
};

If you omit boost:: (i.e., if the call is unqualified), you fall into a completely different bucket, namely #4, which cannot match a function template at all, ever, not even with pretty please. I'll bet you dollars to donuts that just about everyone on our beautiful planet will agree with me that it's Pretty Surprising that just omitting a namespace name changes the meaning of the friend declaration so drastically. Let's avoid such edgy constructs.

3. Bucket #3 is a really edgy case, fragile and surprising to most compilers reading your code.

Let's try the two options, bucket #1 and bucket #3, on a wide range of current compilers and see what they think. Will the compilers understand the Standard as well as we do (having read the above)? Will at least all the strongest compilers do what we expect? No, and no, respectively.

Let's try bucket #3 first:

// Example 1 again
//
namespace boost {
  template<typename T> void checked_delete( T* x ) {
    // ... other stuff ...
    delete x;
  }
}
class Test {
  ~Test() { }
  friend void boost::checked_delete( Test* x ); // the original code
};

int main() {
  boost::checked_delete( new Test );
}

Try the above on your own compiler and then compare with our results. If you've ever watched the game show "Family Feud" on television, you can now imagine Richard Dawson's voice saying: "Survey saaaaays" (see Table 1).

In this case, the survey says that this syntax is not well recognized on actual compilers. By the way, it shouldn't surprise us that Comeau, EDG, and Intel all agree, because they're all based on the EDG C++ language implementation; of the five distinct C++ language implementations tested here, three don't accept this version (gcc, Metrowerks, Microsoft) and two do (Borland, EDG).

Let's try writing it the other standards-conforming way, for bucket #1:

// Example 2: The other way to declare friendship
//
namespace boost {
  template<typename T> void checked_delete( T* x ) {
    // ... other stuff ...
    delete x;
  }
}
class Test {
  ~Test() { }
  friend void boost::checked_delete<>( Test* x );
};

int main() {
  boost::checked_delete( new Test );
}

Or, equivalently, we could have spelled out:

  friend void boost::checked_delete<Test>( Test* x );

Either way, when we twist our compilers' tails, our survey says that this is noticeably better supported (see Table 2).

Bucket #1 sure feels safer — Example 2 works on every current compiler except gcc, and every older compiler except Microsoft Visual C++ 6.0.

Aside: It's the Namespace That's Confusing Them

Note that if the function template we're trying to befriend wasn't in a different namespace, then we could use bucket #1 correctly today on nearly all these compilers:

// Example 3: If only checked_delete
// weren't in a namespace...
//
// No longer in boost::
template<typename T> void checked_delete( T* x ) {
  // ... other stuff ...
  delete x;
}

class Test {
  // No longer need "boost:"
  friend void checked_delete<Test>( Test* x );
};

int main() {
  checked_delete( new Test );
}

Survey says ... (see Table 3). So the problem on most compilers that can't handle Example 1 is specifically declaring friendship for a function template specialization in another namespace. (Whew. Say that three times fast.) Alas, the poster's compiler, Microsoft Visual C++ 6.0, can't handle even this simpler case.

Two Non-Workarounds

When this question arose on Usenet, some responses suggested writing a using-declaration (or equivalently a using-directive) and making the friend declaration unqualified:

namespace boost {
  template<typename T> void checked_delete( T* x ) {
    // ... other stuff ...
    delete x;
 }
}

using boost::checked_delete;

class Test {
  ~Test() { }

  // NOT the template specialization!
  friend void checked_delete( Test* x );
};

The above friend declaration falls into bucket #4 above: "4. ELSE the name must be unqualified and declare (or redeclare) an ordinary (non-template) function." This is actually declaring a brand-new ordinary non-template function at the enclosing namespace scope called ::checked_delete( Test * ).

If you try the above code, many of these compilers will reject it saying that checked_delete() hasn't been defined, and all of them will reject it if you actually try to make use of the friendship and put a private member access call into the boost::checked_delete() template.

Finally, one expert suggested changing it slightly — using the "using" but also using the template syntax "<>":

namespace boost {
  template<typename T> void checked_delete( T* x ) {
    // ... other stuff ...
    delete x;
 }
}

using boost::checked_delete;

class Test {
  ~Test() { }
  friend void checked_delete<>( Test* x ); // legal?
};

The above is probably not legal C++ — the Standard is not clear that this is legal, there's an open issue in the standards committee to decide whether or not this ought to be legal, there is sentiment that it should not be legal, and in the real world virtually all current compilers that I tried reject it. Why do people feel that it should not be legal? For consistency, because using exists to make it easier to use names — to call functions and to use type names in variable or parameter declarations. Declarations are different: just as you must declare a template specialization in the template's original namespace (you can't do it in another namespace "through a using"), so you should only be able to declare a template specialization as a friend naming the template's original namespace (not "through a using").

Summary

To befriend a function template specialization, you can choose one of two syntaxes:

  // From Example 1
  friend void boost::checked_delete ( Test* x );

  // From Example 2: add <> or <Test>
  friend void boost::checked_delete<>( Test* x );

This article has demonstrated a pretty high portability price to pay in practice for not just writing "<>" or "<Test>" as in Example 2.

Guideline: Say what you mean, be explicit. When you befriend a function template specialization, always explicitly add at least the "<>" template syntax. For example:

namespace boost {
  template<typename T> void checked_delete( T* x );
}
class Test {
  friend void boost::checked_delete ( Test* x ); // BAD
  friend void boost::checked_delete<>( Test* x ); // GOOD
};

If your compiler doesn't allow either of these legal alternatives for the friend declaration yet, however, you'll have to make the necessary function(s) public — but add a comment saying why and make a note to change it back to private as soon as you upgrade your compiler [1].

Acknowledgment

Thanks to John Potter for comments on drafts of this material.

Note

[1] There are other workarounds, but they're all much more cumbersome. For example, you could create a proxy class inside namespace boost and befriend that.

About the Author

Herb Sutter (<www.gotw.ca>) is convener of the ISO C++ standards committee, author of the acclaimed books Exceptional C++ and More Exceptional C++, and one of the instructors of The C++ Seminar (<www.gotw.ca/cpp_seminar>). In addition to his independent writing and consulting, he is also C++ community liaison for Microsoft.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video