模板友元化
2007-03-16 21:57:45 来源:WEB开发网因为,问题——大多数编译器上不能处理例1――产生于在另一个命名空间中明确地声明了某个函数模板的特化。(喝倒彩三声?:)微软的Visual C++ 6.0 编译器甚至不能处理最简单的情况。
两种错误的答案(Non-Workarounds)
当这个问题在Usenet被提出时,一些人的回复中建议用一个using声明(或者等价地using指示),去掉友元声明的作用域限定:
namespace boost {
template<typename T> void checked_delete( T* x ) {
// ... 其它代码 ...
delete x;
}
}
using boost::checked_delete;
class Test {
~Test() { }
// 没有模板特化!
friend void checked_delete( Test* x );
};
上边的友元声明又落入了#4的形式:"4.否则,友元的名字必须不被冠以作用域修饰,而是声明为一个常规函数(非模板)。"这实际上是在全局命名空间中声明了一个新的常规非模板函数::checked_delete(Test*)。
如果你试试上边的代码,上述数编译中的大多数器都会拒绝它,并提示checked_delete()没有被定义;而且它们全部都会拒绝让你在boost::checked_delete()模板中以友元的身份去调用类的私有成员。
最后,一位专家建议把它稍稍改一下——使用"using"也是用模板语法"<>":
namespace boost {
template<typename T> void checked_delete( T* x ) {
// ... 其它代码 ...
delete x;
}
}
using boost::checked_delete;
class Test {
~Test() { }
friend void checked_delete<>( Test* x ); //合法么?
};
上边不是合法的C++代码——C++标准没有明确指出这是合法的。在标准委员会中,曾经有一过一次公开的讨论——以决定该用法是否合法,存在一个观点认为它应该是非法的,因为事实上所有我测试过的当前编译器都拒绝它。为什么人们认为它不能是合法的呢?为了保持一致性,因为using的存在是为了令名字使用起来更加容易——调用函数/在变量或参数声明中使用类型名。声明有所不同的是:正如你必须在模板的原始作用域中声明该模板的一个特化一样,(你不能在另一个命名空间中通过"using"来达到这一目的),你只能将一个模板的特化声明为——冠以该模板作用域的——友元(而不能通过"using"来做到这一点)。
总结
为了友元化一个函数模板的特化,应该选择如下2种语法之一:
// 来自例1
friend void boost::checked_delete ( Test* x );
// 来自例2:增加<>或<Test>
friend void boost::checked_delete<>( Test* x );
本文演示了——不像例2所示,写上"<>"或"<Test>"的代码所产生的——严重的移植性问题。
方针:说明白你到底想要什么。(Guideline:Say what you mean, be explicit.)
当你友元化一个函数模板的特化时,应该总是清楚地冠以模板的语法,至少加上"<>"。例如:
namespace boost {
template<typename T> void checked_delete( T* x );
}
class Test {
friend void boost::checked_delete ( Test* x ); // 不好
friend void boost::checked_delete<>( Test* x ); // 好
};
如果你的编译器不支持这两种声明友元的合法语法的话,你就要把必要的函数声明为公共的了――不过,应该加上一条注释以说明原因,并提醒自己一旦编译器升级了的话,便应尝试将这些函数声明改回成私有的。
承谢
感谢John Potter对本文草稿的审校。
注释
[1] 有其它的实现方式,但却笨拙。例如:可以在命名空间boost中创建一个代理类并对其友元化。
更多精彩
赞助商链接