返回   cpper编程论坛 > C/CPP/TMP/GP
注册账号 论坛帮助 会员列表 日历事件 搜索 今日新帖 标记版面已读

回复
 
LinkBack 主题工具 显示模式
  #1 (permalink)  
旧 2005-06-27
高级会员
 
注册日期: 2002-09-16
帖子: 1,087
文章: 1
SpitFire 正向着好的方向发展
默认 刚看了exceptional c++ style的第八个item

增加一个模范函数为一个类的友员作了详细说明,并列出了不同的用法

vc6全不支持,vc7.1有一个不支持,但至少支持标准做法
贴一下下
代码:
Solution This Item 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 gcc. Let's investigate. The Original Attempt Show the obvious standards-conforming syntax for declaring boost::checked_delete as a friend of Test. This Item was prompted by a question on Usenet by Stephan Born, who wanted to do just that. 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. Here's his original code: // Example 8-1: One way to grant friendship // class Test { ~Test() { } friend void boost::checked_delete(Test* x); }; 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 8-1's friend declaration has the following characteristics: It's legal according to the standard but relies on a dark corner of the language. It's rejected by many current compilers, including very good ones. It's 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 then finish with a guideline for how to write the most portable code. Why It's Legal But Dark Why is the obvious syntax unreliable in practice? Describe the more reliable alternative. When declaring friends, there are four options (enumerated in the [C++03] §14.5.3). They boil down to this: When you declare a friend without saying the keyword template anywhere: 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. 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. 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. Else the name must be unqualified and declare (or redeclare) an ordinary (nontemplate) function. Clearly #2 and #4 match only 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); or // Adding "<Test>", still legal because it falls into bucket #1 friend void boost::checked_delete<Test>(Test* x); 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. 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, even though it's technically legal. Issue 1: It Doesn't Always Work As already noted, the Bucket #3 syntax is 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, isn't it? Kind of easy to mistake, isn't it? Let's avoid such subtleties. Issue 2: It's Surprising to People Bucket #3 is edgy and fragile and surprising to programmers who look at the code and try to figure out what it does. 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. Issue 3: It's Surprising to Compilers Bucket #3 is edgy and fragile and surprising to compilers, and that can make it unusable in practice even if we disregard the other shortcomings mentioned earlier. 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 this Item so far)? Will at least all the strongest compilers do what we expect? No and no, respectively. Let's try bucket #3 first: // Example 8-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 this 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 8-1). Table 8-1. The results of compiling Example 8-1 on various compilers Compiler Result Error message Borland 5.5 OK Comeau 4.3.0.1 OK Digital Mars 8.38 Error Symbol Undefined ?checked_delete@@YAXPAV-Test@@@Z (void cdecl checked_delete(Test *)) EDG 3.0.1 OK Intel 6.0.1 OK gcc 2.95.3 Error `boost::checked_delete(Test *)' should have beendeclared inside `boost' gcc 3.4 Error `void boost::checked_delete(Test*)' should 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 'boost::checked_delete' specified as friend MS VC++ 7.0 (2002) OK MS VC++ 7.1 (2003) Error 'boost::checked_delete' : not a function MS VC++ 8.0 (2005) beta OK In this case, the survey says that this syntax is not well recognized on actual compilers; one implementation changed its mind several times across versions. 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 (Digial Mars, gcc, Metrowerks), two do (Borland, EDG), and one varies across versions (Microsoft). Let's try writing it the other standards-conforming way, for bucket #1: // Example 8-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); // the alternative }; int main() { boost::checked_delete(new Test); } Or, equivalently, we could have spelled out: friend void boost::checked_delete<Test>(Test* x); // equivalent Either way, when we twist our compilers' tails, our survey says that this is noticeably better supported (see Table 8-2.). Table 8-2. The results of compiling Example 8-2 on various compilers Compiler Result Error message Borland 5.5 OK Comeau 4.3.0.1 OK Digital Mars 8.38 OK EDG 3.0.1 OK Intel 6.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.4 Error `void boost::checked_delete(Test*)' should have been declared inside `boost' Metrowerks 8.2 OK MS VC++ 6.0 Error nonexistent function 'boost::checked_delete' specified as friend MS VC++ 7.0 (2002) OK MS VC++ 7.1 (2003) OK MS VC++ 8.0 (2005) beta OK Bucket #1 sure feels safer?Example 8-2 works on every current compiler except gcc, and every older compiler except MS VC++ 6.0. Aside: It's the Namespace That's Confusing Them Note that if the function template we're trying to befriend weren't in a different namespace, we could use bucket #1 correctly today on nearly all these compilers: // Example 8-3: If only checked_delete weren't in a namespace… // template<typename T> void checked_delete(T* x) { // no longer in boost:: // … other stuff … delete x; } class Test { friend void checked_delete<Test>(Test* x); // no longer need "boost:" }; int main() { checked_delete(new Test); } Survey says (see Table 8-3). Table 8-3. The results of compiling Example 8-3 on various compilers Compiler Result Error message Borland 5.5 OK Comeau 4.3.0.1 OK Digital Mars 8.38 OK EDG 3.0.1 OK Intel 6.0.1 OK gcc 2.95.3 OK gcc 3.4 OK Metrowerks 8.2 OK MS VC++ 6.0 Error syntax error (just can't handle it) MS VC++ 7.0 (2002) Error friend declaration incorrectly interpreted as declaring a brand-new (and undefined) ordinary nontemplate function, even though we used template syntax MS VC++ 7.1 (2003) OK MS VC++ 8.0 (2005) beta OK So the problem on most compilers that can't handle Example 8-1 is specifically declaring friendship for a function template specialization in another namespace. (Whew.Say that three times fast.) 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; // or "using namespace boost;" class Test { ~Test() { } friend void checked_delete(Test* x); // not the template specialization! }; This friend declaration falls into bucket #4: "4. Else the name must be unqualified and declare (or redeclare) an ordinary (nontemplate) function." This is actually declaring a brand-new, ordinary nontemplate function at the enclosing namespace scope called ::checked_delete(Test *). If you try this 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; // or "using namespace boost;" class Test { ~Test() { } friend void checked_delete<>(Test* x); // legal? }; This code 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 be able to declare a template specialization as a friend only by 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 8-1 friend void boost::checked_delete (Test* x); // From Example 8-2: add <> or <Test> friend void boost::checked_delete<>(Test* x); // or "<Test>" This Item has demonstrated a pretty high portability price to pay in practice for not just writing <> or <Test> as in Example 8-2. Guidelines Say what you mean. Be explicit. If you're talking about a template and there's any question about what you mean, include a (possibly empty) template argument list. Avoid the dark corners of the language, including constructs that might be arguably legal but that are liable to confuse programmers, or even compilers. 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 yet allow either of these legal alternatives for the friend declaration, however, you'll have to make the necessary function(s) public[12]?but add a comment saying why, and make a note to change it back to private as soon as you upgrade your compiler. [12] 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.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #2 (permalink)  
旧 2005-06-27
wqqafnd 的头像
高级会员
 
注册日期: 2004-10-08
帖子: 196
文章: 1
wqqafnd 正向着好的方向发展
发送 MSN 消息给 wqqafnd
默认

是不是电子书?麻烦一下,给我传一份,多谢了!
wqqafnd@163.com
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #3 (permalink)  
旧 2005-06-29
cppof286 的头像
高级会员
 
注册日期: 2004-11-24
住址: 已经在北京了
帖子: 147
cppof286 正向着好的方向发展
发送 MSN 消息给 cppof286
默认

似乎 GCC 不能正确分析 boost::这个名字空间限定符,但是只要能够避免手工指定就可以了,所以 例子 8-2 过不去但是 8-3 就可以通过。

看来如果在 GCC 环境下,还是尽可能使用 using boost::checked_delete; 之类的语句比较保险。

其它的编译器我就没有了,各位前辈畅所欲言吧。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
回复

书签

主题工具
显示模式

发帖规则
不可以发表新主题
不可以发表回复
不可以上传附件
不可以编辑自己的帖子

启用 BB 代码
论坛启用 表情符号
论坛启用 [IMG] 代码
论坛禁用 HTML 代码
Trackbacks are 启用
Pingbacks are 启用
Refbacks are 启用



所有时间均为格林尼治时间 +9。现在的时间是 06:50 AM


Powered by vBulletin® 版本 3.7.0
版权所有 ©2000 - 2009,Jelsoft Enterprises Ltd.
(C) Copy Right All Right Reserved 2001 - 2007

Search Engine Friendly URLs by vBSEO 3.1.0