【问题标题】:operator<<(ostream& os, ...) for template class模板类的运算符<<(ostream& os, ...)
【发布时间】:2013-04-22 09:17:13
【问题描述】:

为什么我不能对接受模板参数的友元函数使用相同的模板参数?我的意思是下面的代码没问题!

template <class Vertex>
class Edge
{
   template <class T>
   friend ostream& operator<<(ostream& os, const Edge<T>& e);
   /// ...
};


template <class T>
ostream& operator<<(ostream& os, const Edge<T>& e)
{
   return os << e.getVertex1() << " -> " << e.getVertex2();
}

但是这个不行。为什么?问题是什么? (我得到链接器错误。)

template <class Vertex>
class Edge
{
   friend ostream& operator<<(ostream& os, const Edge<Vertex>& e);
   /// ...
};

template <class T>
ostream& operator<<(ostream& os, const Edge<T>& e)
{
   return os << e.getVertex1() << " -> " << e.getVertex2();
}

【问题讨论】:

  • 一个朋友是模板,另一个不是。

标签: c++ templates operators


【解决方案1】:

你可以使用以下

template <class Vertex>
class Edge
{
   friend ostream& operator<< <>(ostream& os, const Edge<Vertex>& e);
   /// ...
};

这使 operator &lt;&lt; &lt;Vertex&gt; 成为 Edge 的朋友。

在你的第二种情况下 - 你交朋友非模板运算符,但这个运算符的定义是模板,所以你有未定义的引用,但是如果你想要你的operator &lt;&lt; 来具体Edge(@例如 987654326@)。

【讨论】:

  • 这需要operator&lt;&lt;函数模板的前向声明(这反过来又需要Edge类模板的前向声明)。
【解决方案2】:
template <class T>
friend ostream& operator<<(ostream& os, const Edge<T>& e);

说,外面有templatedoperator &lt;&lt;,和它做朋友,一切OK。

 

 friend ostream& operator<<(ostream& os, const Edge<Vertex>& e);

说,外面有个operator &lt;&lt;,和它做朋友吧……编译器找不到这样的东西。

要告诉编译器该运算符是模板化的,请通过 &lt;&gt; 帮助他,正如 ForEveR 提到的(他迅速击败了我:-D)。

【讨论】:

    【解决方案3】:

    问题出在这里:

    friend ostream& operator<<(ostream& os, const Edge<Vertex>& e);
    

    您正在声明一个非模板函数(它将是 Edge) 的每个实例化都不同为 friend, 而不是模板的实例化。

    我在这里看到的最常见的解决方案是简单地 在类模板中实现operator&lt;&lt; 内联 定义。或者,您可以提供一个公共成员 执行输出的函数,并从 operator&lt;&lt; 函数模板。或者你可以写:

    friend ostream& operator<< <Vertex>(ostream&, Edge<Vertex> const& );
    

    告诉编译器operator&lt;&lt;是朋友 模板的实例化。但是,IIUC 仅适用 如果有目标operator&lt;&lt; 函数的声明 此时模板可见,这意味着您需要 转发声明它(并且为了转发声明它,到 前向声明类模板)。

    对于此类问题,我通常的解决方案是提供 一个普通的成员函数print,然后派生自:

    template <typename DerivedType>
    class IOStreamOperators
    {
    public:
        friend std::ostream&operator<<(
            std::ostream&       dest,
            DerivedType const&  source )
        {
            source.print( dest ) ;
            return dest ;
        }
    
        friend std::istream&operator>>(
            std::istream&       source,
            DerivedType&        dest )
        {
            dest.scan( source ) ;
            return source ;
        }
    
    protected:
        ~IOStreamOperators() {}
    };
    

    ,例如:

    template <class Vertex>
    class Edge : public IOStreamOperators<Edge<Vertex> >
    {
        // ...
        void print( std::ostream& dest )
        {
            //  ...
        }
    };
    

    我发现这通常会使代码更简单、更容易 到最后。

    【讨论】:

      【解决方案4】:

      我认为如果我们去除无关的噪音并考虑:

      template <typename T>
      struct X
      {
          friend void f(X<T>& x) { }
      };
      
      template <typename T>
      void f(const X<T>& x) { }
      
      • f里面的X是:void f(X&lt;T&gt;&amp; x)
      • X外的f是:void f&lt;T&gt;(X&lt;T&gt;&amp; x)

      您可以通过编译并查看生成的符号来了解这一点:

      00410aa8 t .text$_Z1fR1XIdE
      00410ad4 t .text$_Z1fIdEvRK1XIT_E
      

      从每个收益中调用 GCC 的 __PRETTY_FUNCTION__

      void f(X<double>&)
      void f(const X<T>&) [with T = double]
      

      不是特别清楚,但GCC的说法是后者的void f&lt;double&gt;(...)

      就个人而言,对于模板我倾向于在类中定义函数...你根本不需要提及模板方面,只是:

      friend ostream& operator<<(ostream& os, const Edge& e)
      {
          // use e.whatever...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-17
        • 2023-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-12
        相关资源
        最近更新 更多