【问题标题】:Friend operator in template struct raises redefinition error模板结构中的朋友运算符引发重新定义错误
【发布时间】:2014-10-08 00:57:02
【问题描述】:

考虑这段代码:

template<typename T,typename K>
struct A{

   friend std::ostream& operator<<(std::ostream& out, K x) { 
      // Do some output 
      return out; 
   }

};

int main(){
   A<int,int> i;
   A<double,int> j;
}

它没有编译,因为A的两个实例化operator&lt;&lt;两次使用相同的签名,所以我收到这个错误:

test.cpp:26:25: error: redefinition of ‘std::ostream& operator<<(std::ostream&, int)’
    friend std::ostream& operator<<(std::ostream& out, K x) { return out; }
                         ^
test.cpp:26:25: error: ‘std::ostream& operator<<(std::ostream&, int)’ previously defined here

如何解决这个问题?当该运算符 可能 对两个不同的实例具有相同的签名时,如何在模板中拥有一个朋友运算符?如何在不触发重定义错误的情况下解决这个问题?

【问题讨论】:

  • operator&lt;&lt;的定义中需要T吗?
  • int 已经可以与operator&lt;&lt; 一起使用。由每个班级提供合适的operator&lt;&lt;
  • 我真的不明白你为什么要为 K 实现 operator&lt;&lt;,尤其是为什么你需要它与(几乎)不相关的类 A&lt;T,K&gt; 成为朋友。正如 Neil 所说,由 K 提供运算符的实现,而 int 已经有了。
  • 你没有。您在类中定义与类相关的操作,而不是其模板化参数。
  • 我同意 CashCow 的观点。在您的示例中,流插入器实际上不需要成为struct A 的朋友。它可能需要成为K 的朋友,但它不需要了解A 的内部结构。

标签: c++ templates c++11 operator-overloading


【解决方案1】:

我真的不认为像这样宣布朋友有任何用处,但你可以这样做:

template<typename T, typename K>
struct A{
  template<typename L>
  friend std::ostream& operator<<(std::ostream& out, L const &x);
};

template<typename T>
std::ostream& operator<<(std::ostream& out, T const &x) {
  // ... 
  return out;
}

LIVE DEMO

编辑:

另一个可能更接近您想要的选项是:

template<typename T>
std::ostream& operator<<(std::ostream& out, T const &x);

template<typename T, typename K>
struct A{
  friend std::ostream& operator<<<K>(std::ostream& out, K const &x);
};

template<typename T>
std::ostream& operator<<(std::ostream& out, T const &x) { 
  // ...
  return out;
}

LIVE DEMO

但真的不知道你为什么想要这个。恕我直言,您的设计存在严重缺陷。

【讨论】:

  • 您的回答也没有任何实际意义。不知道为什么它得到了 7 个赞
  • @CashCow 好吧,它没有多大意义,但回答了问题/解决了问题:朋友运算符是一个模板一个类(模板实例)。因此允许多个定义
  • 这里的友元操作符根本没有定义任何与类相关的东西。 cout 有什么关系?更重要的是,它与 A ? 有关
  • @CashCow 真正的问题是为什么有人想做这样的事情。您认为这种设计有什么实际用途吗?
  • 我根本看不到用户想要达到的目标。假设 A 有一些私有成员(如果没有,您不需要交任何朋友),您可以声明友谊以授予对参数类型的流函数或所有流函数的访问权限。尝试实现流是完全错误的,并且超出了类的范围。
【解决方案2】:

您的问题在以下方面有问题。

假设它是一个存根,并且您的类有一些私有的东西,因此它需要声明一个朋友,这样做的目的是该朋友在类本身的外部,但可以访问其中的私有内容。

在您的情况下,您将参数的流式函数声明为友元。没事儿。这意味着如果有人创建了一个 Bar 类并想要定义 Bar 的流式传输方式,他们的实现可以访问 A&lt;T,Bar&gt; 中任何类型 T 的任何内容。

您的模板与operator&lt;&lt;( ostream&amp;, int) 的冲突实际上不是问题,因为编译器知道该选择哪一个。它总是会选择完全匹配的非模板而不是模板。您的问题是您有 2 个模板,编译器无法在它们之间进行选择,因为它们都同样有效。

也许这就是你真正想要实现的目标

template< typename X, typename Y >
struct A
{
   friend void a_print( std::ostream& Y const & ); // foo external function with Y as parameter, can access this
};

std::ostream & operator<<( std::ostream & out, Bar const& bar )
{
   a_print( out, bar );
   return out;
}

void a_print( Bar const& bar, std::ostream & out )
{
   // implement and call private members of A<Foo, Bar>
   return out;
}

您可以将流式传输模板设为好友,并使用特定实现实现该模板以供 Bar 流式传输。

【讨论】:

    【解决方案3】:

    将方法分解为基类:

    template <typename K>
    struct ABase
    {
       friend std::ostream& operator<<(std::ostream& out, K x) { 
          // Do some output 
          return out; 
       }
    };
    
    template <typename T,typename K>
    struct A : public ABase<K>
    {};
    

    【讨论】:

      【解决方案4】:

      如果你想流式传输 A 对象,正确的签名应该是

      template<typename T,typename K>
      struct A{
      
         friend std::ostream& operator<<(std::ostream& out, const A& x) { 
            // Do some output 
            return out; 
         }
      
      };
      

      获取您可以访问私人成员的A&lt;T,K&gt;,而不是K

      如果没有办法从 K 中得到 A(如果 Kint 则不可能),让 &lt;&lt;(ostream&amp;,K) 成为 A 的朋友没有任何好处。

      【讨论】:

      • 您的声明有效吗?也许标准已经改变,要让它以前工作,你必须向前声明模板并在朋友声明中使用模板
      • 在名称范围内, 而不仅仅是 A。但是在前面有 template 或者在外面重新定义函数是没有必要的(除非函数是很多...,所以玩中立归档听起来更好)。在这种情况下,您声明朋友和模板并将其实现为模板在A的主体之外
      • 我记得以前我需要做:朋友模板 std::ostream& operator
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-17
      • 1970-01-01
      • 2014-10-07
      • 1970-01-01
      • 1970-01-01
      • 2014-12-07
      相关资源
      最近更新 更多