// This is what I saw in the mailing list in the cpptips website, the collection! :-)
Title: Befriending a Template in Another Namespace
(Source: Comp.lang.c . Moderated, 6 OCT 2002)
-------------------------------------------------- ------------------------------ Clarification: The Code in A C Tip Does Not Imply That I think the code exhibits good programming .? practice or is of production quality, only that there is worthwhile technical content For past tips, see http://cpptips.hyperformix.com -Allan "Need help meeting a development deadline See our corporate sponsor ProSpring Technical Staffing at http: / / www.prospring.net ------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[SNIP]
Author: Herb SUTTER (www.gotw.ca)
THERE HAVEEN SEVERAL ANSWERS TO this Question, But They're Not Quite Right. This is an intenting question, so let me try to Treat in a little more depth.
Befriending Templates ---------------------
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
NOW, SAY You want to use this function template with a class where the design in question (Here The Destructor) Happens To Be Private:
// Example 1: no friends // class test {~ test () {} // private!};
Test * t = new test; boost :: checked_delete (// error: Test's destructor is private, // so checkage_delete can't call it.the solution is dead_delete () a friend of test. (The only Other Option IS To Give Up and make test's designructor public.)
The Reason That I'm Writing An Article About It Is Because, Alas, Befriending A Template In Another Namespace IS Easier Said Than Done:
- The Bad News: There is: The aten............... ..
- The Good News: One of the Perfectly Good Standards-Conforming Ways Does Work On Every Current Compiler I Tried Except for GCC.
THE Original Attempt --------------------
Here's The Original Code:
Stephan born
// EXAMPLE 2: One Way to Grant Friendship (?) // Class Test {~ TEST ()} Friend Void Boost :: Checked_Delete (Test * x);
Alas, This Code Didn't Work on The Poster's Compiler (VC 6.0). In Fact, IT Fails on Quite A Few Compilers. In Brief, Example 2's Friend Declaration:
- Is Technically Legal 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)
Why it's legal but dark -----------------------
When Decilang Friends (Enumerated In The C Standard, Clause 14.5.3). They Boil Down To this:
WHEN You Declare A Friend With Saying The Keyword "Template" Anywhere:
1. IF the name of the friend looks like the name of a template specialization with explicit template arguments (eg, Name
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 order of ord-ary (non-template) function.
Clearly # 2 and # 4 only match nontemplates, 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);
oral
// adding "
The first is shorthand for the second ... but ONLY IF the name is qualified (here by "boost ::") AND there's no matching nontemplate function in the same indicated scope. This dark corner of the friend declaration rules is sufficiently surprising to People - And to Most Current Compilers! - That I will propose no fewer 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:
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 nontemplate function.
In particular, if the namespace has (or later gets) a matching nontemplate function, that would get chosen instead because the presence of a nontemplate function means bucket # 2 preempts # 3. Kind of subtle and surprising, is not 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 // Class test {~ test () {} friend void check_delete (test * x); // Ouch: Legal, But not what you}; // want. More about this later.
If You Omit "Boost ::" (IE, 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 STRONGEST COMPILERS DO What We Expect? no, and no, respectively.
Let's try bucket # 3 first:
// eXample 2 again // namespace boost {template
Borland 5.5 OK Comeau 4.3.01 OK Edg 3.0.1 OK GCC 2.95.3 Error `Boost :: Checked_delete (Test *) 'Should Have Been Declared Inside` Boost' GCC 3.1.1 Error `Void Boost :: Checked_delete (Test * ) 'should have been declared inside `boost' gcc 3.2 ERROR` void boost :: checked_delete (Test *) 'should have been declared inside `boost' Intel 6.0.1 OK Metrowerks 8.2 ERROR friend void boost :: checked_delete (Test * x ); name has not been declared in namespace / class MS VC 6.0 ERROR nonexistent function 'boost :: checked_delete' specified as friend MS VC 7.0 OK MS VC 7.1beta ERROR 'boost :: checked_delete': not a function
For MS VC 6.0, the error is what the original poster reported. But you'll get the same (or similar) error on some pretty strong and conformant compilers, including Metrowerks 8.2, g 3.2, and MS VC 7.1. (All of these versions were released, or went into beta, in the past month or two.) By the way, it should not surprise us that Comeau, EDG, and Intel all agree, because they're all based on the EDG C language implementation. IF We Collapse The List So We Count Only Distinct Compilementations (IE, All of the One ", And Take Code Base, And Take The Latest of Each, IT Looks More Like this:
Borland Ok Edg-Based Compilers OK GCC (All Versions) Error Metrowerks Error MS VC Error
SO, IN Short, MOST C Language Implementations Don't Accept this version.
Let's Try Writing It The Other Standards-Conforming Way, for Bucket # 1:
// Example 3: The Other Way to Declare Friendship // Namespace Boost {Template
Borland 5.5 OK Comeau 4.3.01 OK Edg 3.0.1 OK GCC 2.95.3 Error `Boost :: Checked_delete (Test *) 'Should Have Been Declared Inside` Boost' GCC 3.1.1 Error `Void Boost :: Checked_delete (Test * ) 'should have been declared inside `boost' gcc 3.2 ERROR` void boost :: checked_delete (Test *) 'should have been declared inside `boost' Intel 6.0.1 OK Metrowerks 8.2 OK MS VC 6.0 ERROR nonexistent function 'boost :: checked_delete 'specified as friend MS VC 7.0 OK MS VC 7.1beta OKIf we collapse the list so that we count only distinct compiler implementations (ie, all of the ones that use EDG are the same code base), and take the latest of each, IT Looks More Like this:
Borland Ok Edg-Based Compilers OK GCC (All Versions) Error Metrowerks OK MS VC 7.0 & Higher OK
Bucket # 1 sure feels safer -. This works on every current compiler except gcc, and every older compiler except MS VC 6.0 (This might not be the most useful answer for the OP, who is using MS VC 6.0 which offers no way to Declare the friends; "And USE MS VC 7.x.)
Aside: it's the namespace That's confusing them -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----
Note that if the function template we're trying to befriend was not in a different namespace, then we could use bucket # 1 correctly today on all these compilers: // Example 4: If only checked_delete were not in a namespace .. . // no longer in boost :: // ... Other stuff ... delete x;}
Class test {friend void checked_delete
Borland 5.5 OK Comeau 4.3.01 OK EDG 3.0.1 OK GCC 2.95.3 OK GCC 3.1.1 OK GCC 3.2 Ok Intel 6.0.1 Ok Metrowerks 8.2 OK MS VC 6.0 Error (Emits a Syntax Error, Just Can't Handle IT ) MS VC 7.0 ok MS VC 7.1beta OK
So the problem on most compilers that can not handle Example 2 is specifically declaring friendship for a function template specialization _in another namespace_. (Whew. Say that three times fast.) Alas, the poster's compiler, MS VC 6.0, can not handle Even this Simpler Case.
Summary -------
The Above DemonStrates a Pretty High Portability Price To Pay Not Just Writing "
Guideline: When you make a function template specialization a friend of a class template, always explicitly write the function template specialization's name including the template arguments For example.:
Namespace Boost {Template
CODA: Notes About Previous Responses ------------------------------------
One of the previously Posted Responses said the following:
> Yes, ITS A Bug. get along with a using:
YES, IT IS A BUG - IN MOST CURRENT Compilers, Including Highly-Regarded Ones. It's a Dark Corner, And We Shouldn't Rely on It.
THE COVPOND BOILS DOW WRITING A Using-Directive) and MAKING THE CALLUALIFIED:
namespace boost {template
The Above Friend Declaration Falls Into Bucket # 4 Above:
4. Else the name must be unqualified and declare (or redeclare) An order of ord-ary (non-template) function.
. Yes, it is indeed unqualified But it does not mean what that poster thought it means (and in fact it works equally well without the "using"): What the friend declaration is in fact doing is declaring a brand-new ordinary non -template function at the enclosing namespace scope called :: checked_delete (Test *) .If you try the above code, many compilers will reject it saying that checked_delete () has not been defined, and all compilers will reject it if you actually try To make use of the friendship and put a private member access call int t Boost :: Checked_delete () template.
The Other Response WAS:
> As a work-around, try declaring & defining an overloaded> Checked_Delete for test.
The suggested code is:
Namespace Boost {Template
Class test; void checked_delete (test * x) {boost :: checked_delete (x);}
Class test {friend void checked_delete (test * x);
This is legal code accepted by all the above compilers, but it does not do what the original poster was asking for, to wit: It does not make the boost :: checked_delete
[1] There isy're all musue more cumbersome. For example, you. For example.