简体   繁体   中英

Friend operator in template struct raises redefinition error

Consider this code:

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

It does not compile, because the two instantiations of A instantiate the operator<< two times with the same signature, so I receive this error:

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

How to fix this? How can I have a friend operator in a template, when that operator might have the same signature for two different instantiations? How can I solve this without triggering a redefinition error?

I don't really see any use in declaring a friend like this, nevertheless this is how you can do it:

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

Edit:

Another option probably closer to what you want will be:

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

But really not sure why you want this. IMHO your design has serious flaws.

Your question is faulty in the following way.

Assuming it's a stub and your class has something private such that it needs to declare a friend, the purpose of doing so is that the friend is external to the class itself but has access to what is private within it.

In your case you are declaring streaming functions of a parameter as a friend. That is fine. It means if someone creates a class Bar and wants to define how a Bar is streamed their implementation may access anything in A<T,Bar> for any type T .

The clashing of your template with operator<<( ostream&, int) is not actually a problem as the compiler knows which one to pick. It will always pick the exact match non-templated over the templated one. Your problem is that you have 2 templated ones and the compiler cannot pick between them because they are both equally valid.

Maybe something like this is what you are really trying to achieve

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

You could make the streaming template a friend and implement the one for Bar to stream using a specific implementation.

Factor the method out to a base class:

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

If you want to stream an A object, the proper signature should have been

template<typename T,typename K>
struct A{

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

};

taking an A<T,K> for which you can access private members, not a K .

If there is no way to get an A from K (and cannot be if K is int ), making <<(ostream&,K) a friend of A gives no benefits.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM