模板类和插入提取重载

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&lt;T&gt;&amp;。 (声明可以使用注入的类名Matrix 来表示Matrix&lt;T&gt;,但这在定义中是不可见的。) @aschepler,应该纠正这个问题。你对第二件事也是正确的——在课堂上我可以说Matrix而不是Matrix&lt;K&gt;。奇怪的是必须在运算符的类声明中使用 T 以外的其他标识符。也许这是我使用的 GCC 版本的一个怪癖,或者它是一个允许您这样做的 Visual Studio 扩展。有人知道标准是怎么说的吗? 您的版本为所有operator&lt;&lt;(ostream&amp;, const Matrix&lt;K&gt;&amp;) 函数授予友谊。仅授予“匹配”实例化友谊: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&lt;&lt; 是在类之外定义的,编译器将在所有找到a &lt;&lt; b 的地方发现它作为重载决议的候选者,只是在所有情况下都丢弃它其中第二个参数不是test&lt;T&gt;(它会在所有无法匹配operator&lt;&lt; 的错误消息中将模板化运算符显示为候选,这已经是一个足够长的列表了)。

【讨论】:

您能否解释一下为什么必须提前声明模板化运算符? @Silverrocker:主要原因是该语言的设计考虑了单遍编译器 (*)。问题在于friend 语句告诉编译器:授予对该模板的特定实例化的访问权。为了使编译器有意义,必须已经声明了模板。它类似于:void foo() bar(5); template&lt;typename T&gt; void bar( T ) 。在声明通用模板和特化之前,您不能使用或引用类模板的特化或实例化。 (*) 这是一个目标,而不是现实,所有编译器出于其他原因需要多次检查输入文件。我在某处读到,对初始代码执行较少传递的那个有 3 次传递——包括预处理器。我无法提供该语句来源的链接,也不记得它是什么编译器。

以上是关于模板类和插入提取重载的主要内容,如果未能解决你的问题,请参考以下文章

求高手帮帮小弟,这个程序错在哪里了?(关于c++的模板类和运算符重载)

C++ 派生模板类:访问实例的受保护成员

9_模板

C++模板函数使用

C++模板类和类模板

类模板 友元重载形式 各种运算符重载 new delete ++ = +=