模板结构中的Friend运算符会引发重定义错误

时间:2014-08-14 11:26:29

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

考虑以下代码:

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<<实例化两次,所以我收到了这个错误:

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

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

4 个答案:

答案 0 :(得分:5)

我在宣布这样的朋友方面没有任何用处,不过这就是你能做到的:

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

但真的不确定你为什么要这样。恕我直言,你的设计有严重的缺陷。

答案 1 :(得分:3)

您的问题以下列方式出错:

假设它是一个存根,你的类有一些私有的东西,它需要声明一个朋友,这样做的目的是朋友是类本身的外部,但可以访问其中的私有。

在您的情况下,您将某个参数的流功能声明为朋友。那样就好。这意味着,如果有人创建了一个类Bar,并希望定义一个Bar的流式传输,那么他们的实现可以访问A<T,Bar>中任何类型T的任何内容。

模板与operator<<( ostream&, int)的冲突实际上并不是问题,因为编译器知道要选择哪一个。它总是会选择非模板化的精确匹配。你的问题是你有两个模板化的,编译器不能在它们之间进行选择,因为它们都同样有效。

也许这样的事情是你真正想要实现的目标

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的流式传输。

答案 2 :(得分:2)

将方法分解为基类:

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>
{};

答案 3 :(得分:0)

如果要传输A对象,则应该使用正确的签名

template<typename T,typename K>
struct A{

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

};

获取A<T,K>,您可以访问私人会员,而不是K

如果无法从K获得A(如果K不是int则无法获得),则<<(ostream&,K)的朋友A不会带来任何好处。