模板类和插入提取重载
Posted
技术标签:
【中文标题】模板类和插入提取重载【英文标题】:template class and insertion extraction overloaded 【发布时间】:2011-05-07 10:53:47 【问题描述】:如何在模板类中重载插入 (>) 运算符而不使其内联。我希望将 > 运算符作为朋友类。 我知道如何使它内联 矩阵类中的内联示例
friend ostream& operator<<(ostream& ostr, const Matrix<T>& inputMatrix)
...
// create the ostr
return ostr;
但我希望将代码放在模板类定义之外。
g++ 告诉我在函数名之后添加 ,所以我这样做了,但是当我尝试实例化 SOMETYPE 类型的矩阵时,它给了我一个错误,它不知道如何为该类型提取或插入。
【问题讨论】:
你应该阅读parashift.com/c++-faq-lite/templates.html#faq-35.16。顺便说一句,作为模板函数,无论如何它都会隐式内联。 下次,请使用编辑窗格顶部的101010
按钮来格式化您消息中的代码。在编辑窗格右侧的“如何编辑”浮动窗口中解释了这一点以及更多内容。
【参考方案1】:
将代码放在标题中,在类定义之外。或者,将其放在.tcc
文件中,并将其包含在标题的底部。
【讨论】:
【参考方案2】:尝试类似:
template <typename T> class Matrix;
template <typename T> std::ostream& operator<<(std::ostream& ostr, const Matrix<T>& m);
template <Typename T>
class Matrix
public:
friend ostream& operator<< <T> (ostream& ostr, const Matrix<K>& inputMatrix);
;
// This must be in the same translation unit as the class definition!
template<typename T>
ostream& operator<<(ostream& ostr, const Matrix<T>& inputMatrix)
// ...
return ostr;
Translation unit reference
重新编辑以解决 aschepler 和 dribeas 制作的 cmets。
【讨论】:
我认为你在函数定义中需要const Matrix<T>&
。 (声明可以使用注入的类名Matrix
来表示Matrix<T>
,但这在定义中是不可见的。)
@aschepler,应该纠正这个问题。你对第二件事也是正确的——在课堂上我可以说Matrix
而不是Matrix<K>
。奇怪的是必须在运算符的类声明中使用 T
以外的其他标识符。也许这是我使用的 GCC 版本的一个怪癖,或者它是一个允许您这样做的 Visual Studio 扩展。有人知道标准是怎么说的吗?
您的版本为所有operator<<(ostream&, const Matrix<K>&)
函数授予友谊。仅授予“匹配”实例化友谊:pastebin.com/my9fSrcJ
Re: the Standard: 14.5.3p1: "类或类模板的朋友可以是函数模板或类模板,函数模板的特化,或类模板,或普通的 (非模板)函数或类。”带有另一个class K
参数的版本将整个函数模板声明为友元。我的 pastebin 链接上的版本将函数模板的特化声明为友元。
本来想在这里写评论的,但是觉得太长了。我的答案中提供了将模板的单个实例化声明为朋友的正确代码。【参考方案3】:
如果你真的想在外部定义运算符,并且只与类型与此模板实例化一致的运算符实例化,正确的语法是:
template <typename T> class test; // forward declare template class
template <typename T> // forward declare the templated operator
std::ostream& operator<<( std::ostream&, test<T> const & );
template <typename T>
class test // define the template
friend std::ostream& operator<< <T>( std::ostream&, test<T> const & ); // befriend
;
template <typename T> // define the operator
std::ostream& operator<<( std::ostream& o, test<T> const & )
return o;
在大多数情况下,将定义从类中提取出来是不值得的,因为您仍然需要在标题中提供它并且需要额外的工作。
另请注意,编译器在查找方面存在细微差别。在类定义中内联函数的情况下,编译器不会发现该函数除非其中一个参数实际上是模板的类型,因此它有效地降低了可见性和数量编译器必须做的工作(如果模板化的operator<<
是在类之外定义的,编译器将在所有找到a << b
的地方发现它作为重载决议的候选者,只是在所有情况下都丢弃它其中第二个参数不是test<T>
(它会在所有无法匹配operator<<
的错误消息中将模板化运算符显示为候选,这已经是一个足够长的列表了)。
【讨论】:
您能否解释一下为什么必须提前声明模板化运算符? @Silverrocker:主要原因是该语言的设计考虑了单遍编译器 (*)。问题在于friend
语句告诉编译器:授予对该模板的特定实例化的访问权。为了使编译器有意义,必须已经声明了模板。它类似于:void foo() bar(5); template<typename T> void bar( T )
。在声明通用模板和特化之前,您不能使用或引用类模板的特化或实例化。
(*) 这是一个目标,而不是现实,所有编译器出于其他原因需要多次检查输入文件。我在某处读到,对初始代码执行较少传递的那个有 3 次传递——包括预处理器。我无法提供该语句来源的链接,也不记得它是什么编译器。以上是关于模板类和插入提取重载的主要内容,如果未能解决你的问题,请参考以下文章