3

My code:

#include <iostream>
using std::cin;
using std::cout;
using std::istream;
using std::ostream;

template<typename T>
class Complex
{
    T real, img;
public:
    Complex():real(0), img(0){}
    friend istream& operator>>(istream& input, Complex& c1);
    friend ostream& operator<<(ostream& output, Complex& c1);
    Complex operator+(Complex& c1);
};

template<typename T>
istream& operator>>(istream& input, Complex<T>& c1)
{
    cout<<"Real: ";
    input>>c1.real;
    cout<<"Imag: ";
    input>>c1.img;
    return input;
}

template<typename T>
ostream& operator<<(ostream& output, Complex<T>& c1)
{
    output<<c1.real<<"+"<<c1.img<<"i";
    return output;
}

template<typename T>
Complex<T> Complex<T>::operator+(Complex<T>& c1)
{
    Complex temp;
    temp.real = this->real + c1.real;
    temp.img = this->img + c1.img;
    return temp;
}

int main()
{
    Complex<int> cmp1;
    cin>>cmp1;
    return 0;
}

The error I'm getting is at cin>>cmp1 which is undefined reference to 'operator>>(std::istream&, Complex<int>&)'. But I can't find anything wrong in my code.

The code works if I make complex a non-template class which uses double and remove all template-related code, so the definition of operator>>() is essentially correct.

What changes when I make Complex a template?

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
Waliul
  • 35
  • 7
  • I removed the outpour of your desperation and added a few relevant tags. I didn't know the answer here, so the problem is not trivial, from my perspective. Happy hacking! – Peter - Reinstate Monica Mar 18 '22 at 06:05

2 Answers2

5

Friend functions are not members so they aren't implicitly templates. Declaration there suggests existence of non-template operator for instantiated type Complex<int>. You may use

template<typename U> 
friend istream& operator>>(istream& input, Complex<U>& c1);

template<typename U> 
friend ostream& operator<<(ostream& output, Complex<U>& c1);
Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • While this is strange (because normally the class template name inside a class template definition refers to the respective instantiation) I would expect a different error message, namely "non-friend operator<<() cannot access private member of Complex". Because that's what a friend declaration does, provide access. But I guess the template operator>> is never instantiated, because language rules, so we end up with a linker error instead. Ugly. – Peter - Reinstate Monica Mar 18 '22 at 06:18
3

The problem is that currently the overloaded friend functions are ordinary functions. If you want to make them as function templates instead then you need to change the friend declarations inside the class to as shown below.

To be more formal, in your original code operator<< and operator>> for class template Complex<> are not function templates, but “ordinary” functions instantiated with the class template if needed. That is, they are templated entities.

There are 2 ways to solve the problem, both of which are given below.

Solution 1

template<typename T>
class Complex
{
    //other members as before
    
    //friend declarations
    template<typename U>
    friend istream& operator>>(istream& input, Complex<U>& c1);
    template<typename V>
    friend ostream& operator<<(ostream& output, Complex<V>& c1);
    
    //other members as before
};

Demo

Solution 2

//forward declarations
template<typename T>
class Complex;
template<typename U>
istream& operator>>(istream& input, Complex<U>& c1);

template<typename V>
ostream& operator<<(ostream& output,const Complex<V>& c1);

template<typename T>
class Complex
{
    T real, img;
public:
    Complex():real(0), img(0){}
    
    //friend declarations
    
    friend istream& operator>> <T>(istream& input, Complex<T>& c1);
    
    friend ostream& operator<< <T>(ostream& output,const Complex<T>& c1);
    
    Complex operator+(Complex& c1);
};

Demo

Jason
  • 36,170
  • 5
  • 26
  • 60
  • So, the original friend declaration basically refers to an unrelated, non-template class `Complex`?? Would such a class with the same name as a class template be permitted at all? Once more those arcane C++ rules, a quagmire. – Peter - Reinstate Monica Mar 18 '22 at 06:09
  • @Peter-ReinstateMonica I have added a 2nd solution to the problem in my edited answer. Check out both the solutions. Now coming back to your question. Technically, in the original code of OP `operator<<` and `operator>>` for class template `Complex<>` are not function templates, but “ordinary” functions *instantiated with the class template if needed*. That is, they are **templated entities**. – Jason Mar 18 '22 at 06:17
  • @Peter-ReinstateMonica `Complex` refers to `Complex`. Actually it would expect an instance which would use related `Complex`argument. E.g. if you had instantiate `Complex` , You have to define a standalone function (non-template) with `Complex` argument to satisfy linker. – Swift - Friday Pie Mar 18 '22 at 06:26
  • @Peter-ReinstateMonica `Complex` is actually `Complex` because of **Injected class names**. You can refer to [Injected class name](https://en.cppreference.com/w/cpp/language/injected-class-name) for more about the topic. – Jason Mar 18 '22 at 06:27