Cuj: suter's mill: befriending templates

Source: Internet
Author: User

Make the template a friend
Author: Herb Sutter
Source: cuj: Sutter's Mill
--------------------------------------------------------------------------------

Suppose we have a function template that executes somethingprivate () on the objects it operates on (). In particular, consider

Boost: checked_delete () function template, which is the object to which it is deleted -- in this process, it calls the object's destructor:
Namespace boost {
Template <typename T> void checked_delete (T * X ){
//... Other stuff...
Delete X;
}
}

Now, if you want to apply this function template to a class, the problem is that the operation you need (here is the destructor) happens to be private.

Of:
// Example 1: no friends
//
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 very simple: you only need to make checked_delete () A friend of test.
(The only other option is to discard [encapsulation] and make the Destructor public ).

I wrote this article for this reason because, alas, it is much easier to make the template in another namespace a friend:
L bad message: there are two methods that fully conform to the standard to implement it, but none can work on all current compilers.
L good news: In addition to GCC, one of them can work on every current compiler I tried.

Initial Attempt
--------------------------------------------------------------------------------
Here is the initial code:
PostgreSQL born <stephan.born@bNeOuSsPeAnM.de> written:
// Example 2: One way to grant friendship (?)
//
Class test {
~ Test (){}
Friend void boost: checked_delete (test * X );
};

Alas, this code cannot work on the poster's Compiler (VC ++ 6.0 ). In fact, it
Not in many compilers. Yes, the youyuan statement in Example 2:
L technically legal, but dependent on a vague point of the [C ++] Language
L rejected by many current compilers, including some very good Compilers
L it is easily corrected to be independent of vague points and to work on almost all current compilers (except GCC)

Why is it legal but vague?

When you declare your friends, there will be four situations (listed in the C ++ standard clause 14.5.3 ). Abstract:
When the keyword "template" is not used in any part of the statement:
1. If the name of a friend looks like a special template version with an explicit parameter (for example, name <sometype> ),
Then, youyuan is an explicit and special version of that template.
2. Second, if the name of a friend is limited by the class name or namespace name (for example, some: Name), and the class or namespace

Contains a matched non-template function,
Then, youyuan is the function.
3. In addition, if the name of a friend is limited by the class name or namespace name (for example, some: Name), and the class or namespace

Contains a matching template function (the ability to export appropriate template parameters ),
Youyuan is a special version of the function template.
4. At last, the name must be without the [class or namespace name] limit and declare (or repeatedly declare)
Common (non-template) functions.

It is clear that #2 and #4 only match non-templates. Therefore, we have two options to declare the special version of the template as a friend:

Rule #1 is satisfied, or rule #3 is satisfied. For our example, select:
// 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 <Test> (test * X );

The first is the second abbreviated form ...... However, only when the name is limited (here it is "Boost:"), and there is no match

The configured non-template functions are in the same scope space. The vague points of the AU yuan declaration Rule make people dizzy-for most of the current compilation

This is also true for devices! I can find at least three reasons to avoid using it.

Why avoid rules #3

There are several reasons to avoid rules #3, even if it is technically legal:
1. Rule #3 does not always work.
As mentioned above, it is a short form of the original rule, but only has a class name or namespace name restriction, and there is no matching non-modulo

Board function.
In particular, if the namespace has (or is obtained later) A matched non-template function, the selection will be changed because the non-Template Function

The rule #2 takes the lead in rule #3. It's subtle and surprising, isn't it? It's easy to make mistakes, isn't it? Let us avoid this

Sample.
2. Rule #3 is really sharp and fragile, and surprised most people who read your code.
For example, consider this very tiny variant-all the changes I make is to remove the qualifier "Boost ":
// Variant: make the name unqualified
//
Class test {
~ Test (){}
Friend void checked_delete (test * X); // ouch: legal, but not what you
}; // Want. More about this later.

If you omit "Boost:" (that is, if the call is unlimited), you will drop into a completely different rule (rule #4)

Does not match the function template at all, which is not fun at all. Bet on 20 yuan, almost everyone on our beautiful planet

I agree with my point of view: It's amazing that I just omitted a namespace name, which greatly changed the statement of youyuan.

Meaning. Let's avoid this sharp object.

Rule #3 sharp and fragile, making most compilers dizzy

Let's use rules #1 and rules #3 to conduct a large-scale experiment on the current compiler to see how they are understood. Compiler pair

Is the standard understanding consistent with ours (after we read so much above )? At least the best compiler will meet our expectations? No,

Whether or not (no, and no, respectively ).

Let's first try the rule #3:
// 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 );
}

Compile the above Code with your compiler and compare it with our results. If you have watched the TV program "Family Feud ",

You can see Richard Dawson's voice: "survey saaaaays ". (See Table 1)
Table 1: The results of compiling Example 1 on various Compilers
Compiler result error message
Borland 5.5 OK
Comeau 4.3.0.1 OK
EDG 3.0.1 OK
Intel 6.0.1 OK
GCC 2.95.3 error 'boost: checked_delete (test *) 'shoshould have been declared inside

'Boost'
GCC 3.1.1 error 'void boost: checked_delete (test *) 'shoshould have been declared inside

'Boost'
GCC 3.2 error 'void boost: checked_delete (test *) 'shoshould have been declared inside 'boost'
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 specified Oost: checked_delete 'specified as friend
Ms vc ++ 7.0 OK
Ms vc ++ 7.1 beta error occurred Oost: checked_delete ': not a function

The test results show that this syntax is not well accepted by the current compiler. By the way, comeau, EDG,

Intel compilers all pass, which is not surprising, because they are all implemented based on edg c ++; in the five different C ++ languages tested

In implementation, three versions are not accepted (GCC, metrowerks, Microsoft), and two can (Borland, EDG ).

Let's try another standard compatible method, test rule #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, equivalent, we can also write:
Friend void boost: checked_delete <Test> (test * X );
Whatever method, when we use our compiler to try (we twist our compilers' tails), the result shows that it is well supported.

More. (See table 2)
Table 2: The results of compiling Example 2 on various Compilers
Compiler result error message
Borland 5.5 OK
Comeau 4.3.0.1 OK
EDG 3.0.1 OK
Intel 6.0.1 OK
GCC 2.95.3 error 'boost: checked_delete (test *) 'shoshould have been declared inside

'Boost'
GCC 3.1.1 error 'void boost: checked_delete (test *) 'shoshould have been declared inside

'Boost'
GCC 3.2 error 'void boost: checked_delete (test *) 'shoshould have been declared inside 'boost'
Metrowerks 8.2 OK
Ms vc ++ 6.0 error nonexistent function specified Oost: checked_delete 'specified as friend
Ms vc ++ 7.0 OK
Ms vc ++ 7.1 beta OK

Rule #1 more confident -- Example 2 works on all current compilers except GCC and all compilers except Microsoft Visual C ++ 6.0

Earlier versions of the compiler.

NARRATOR: "namespace" makes them confused.
--------------------------------------------------------------------------------
Note: if we are trying to make it a friend function template not in a different namespace, we can

Almost all of these compilers correctly make rule #1:
// 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 );
}

Test results show that ...... (See table 3 ). Therefore, most compilers that cannot handle Example 1 are explicitly declaring other namespaces.

A special version of the function template in is youyuan (so the problem on most compilers that can't handle example

1 is specifically declaring friendship for a function template specialization in another

Namespace ). (Sorry, read this sentence three times .) Alas, the poster's compiler -- Microsoft Visual C ++ 6.0, no

Solve such a simple problem.
Table 3: The results of compiling Example 3 on various Compilers
Compiler result error message
Borland 5.5 OK
Comeau 4.3.0.1 OK
EDG 3.0.1 OK
Intel 6.0.1 OK
GCC 2.95.3 OK
GCC 3.1.1 OK
GCC 3.2 OK
Metrowerks 8.2 OK
Ms vc ++ 6.0 error syntax error (just can't handle it)
Ms vc ++ 7.0 error friend declaration incorrectly interpreted as declaring a brand-new

(And undefined) Ordinary non-template function, even though we used template syntax
Ms vc ++ 7.1 beta OK

Two unfeasible Methods
--------------------------------------------------------------------------------
When this problem occurs on USENET, some respondents suggest using-declaration (or, equivalent using-Directive)

And then use the Enis statement with no scope qualifier:

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 preceding statement will fall into rule #4: "At last, the name must be without the [class or namespace name] limit and declare (or

Repeated statement) a common (non-template) function ." This actually declares a new common non-template in a closed namespace.

The function is called: checked_delete (test *).

If you try the above Code, many compilers will reject it, prompting that checked_delete () is not defined; All compilers will reject it, as shown in figure

If you try to use the relationship between friends and friends, put the private member operation call in the boost: checked_delete () template.

Finally, an expert suggested slightly modifying it-using "using" and using the template syntax "<>" at the same time ":

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 Code may not be a legal C ++ code-the standard does not explicitly state that it is legal, and the Standards Committee has an open topic to decide

Determine whether this should be legal, tends to think that it should not be legal; in the real world, all the compilers I 've tried for the current version are

Reject it. Why do people think it should not be legal? For consistency, because the existence of using is

Easy to use name-use a type name to call a function and to declare variables and parameters. The Declaration is different: just as you must be in the template

In the namespace, declare its special version (you cannot "Use a using" to complete it in another namespace). Therefore, you should

You can only declare a special version as youyuan by specifying the namespace where the template is located (you cannot "Use A using") (just

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

Template specialization as a friend naming the template's original namespace (not "through

Using ")).

Summary
--------------------------------------------------------------------------------

To declare that the special version of a function template is youyuan, you can choose one of the 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 proved that if you do not write "<>" or "test" in example 2, you will have to pay a high portability cost.

Guiding principle: explicitly express your intention. When you declare that a specific version of the function template is a friend, at least one pair of"

<> "Template symbol. 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 does not support any of these two Enis, you have to set the required function as public-but add one

Comment out why this is done, and remind you to change it back to private as long as your compiler is upgraded. [NOTE 1]

Thanks
--------------------------------------------------------------------------------
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

Cocould create a proxy class inside namespace boost and befriend that.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.