【问题标题】: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 按钮来格式化您消息中的代码。在编辑窗格右侧的“如何编辑”浮动窗口中解释了这一点以及更多内容。

标签: c++ templates operator-overloading inline insertion


【解决方案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 次传递——包括预处理器。我无法提供该语句来源的链接,也不记得它是什么编译器。
    猜你喜欢
    • 2017-09-11
    • 1970-01-01
    • 2018-04-17
    • 2012-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-10
    • 2018-05-25
    相关资源
    最近更新 更多