10

I'm trying to write the function definition for overloading the operators ">>" and "<<" outside of the class definition, though in the same file, as you can see. I get the following errors:

1>Source.obj : error LNK2019: unresolved external symbol "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,class MyClass<int> &)" (??5@YAAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$MyClass@H@@@Z) referenced in function _main
1>Source.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class MyClass<int>)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@V?$MyClass@H@@@Z) referenced in function _main
1>Source.obj : error LNK2019: unresolved external symbol "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,class MyClass<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > &)" (??5@YAAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$MyClass@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@@Z) referenced in function _main
1>Source.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class MyClass<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@V?$MyClass@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@@Z) referenced in function _main

I've been up and down the river with this one. The strange thing is that if I write their definitions inside the class definition, it works flawlessly.

#include <iostream>
#include <string>
using namespace std;

template <class MYTYPE>
class MyClass {
    MYTYPE *myVector;
    int dim;
    string name;
public:
    MyClass(int, string);
    MyClass() {};
    friend istream& operator>>(istream&, MyClass<MYTYPE>&);
    friend ostream& operator<<(ostream&, MyClass<MYTYPE>);
};

template <class MYTYPE>
MyClass<MYTYPE>::MyClass(int x, string y) {
    dim = x;
    name = y;
    myVector = new MYTYPE[dim];
}


template <class MYTYPE>
istream& operator>>(istream& X, MyClass<MYTYPE>& a){
    cout<<"Reading vector: "<<a.name<<endl;
    for(int indice = 0; indice < a.dim; indice++){
        cout<<a.name<<'['<<indice<<"]= ";
        X >> a.myVector[indice];
    }
    return X;
}

template <class MYTYPE>
ostream& operator<<(ostream& X, MyClass<MYTYPE> a){
    X<<"Vector: "<<a.name<<endl;
    for(int indice = 0; indice < a.dim; indice++)
        X<<a.myVector[indice]<<' ';
        X<<endl;
    return X;
}


int main() {
    MyClass<int> object(4, "Ints vector");
    MyClass<string> object2(5, "String vector");
    cin >> object;
    cin >> object2;
    cout << object;
    cout << object2;
    system("pause");
    return 0;
}
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
NeVada
  • 127
  • 9
  • I might be wrong here, but I don't think you need to include the `MyClass a` argument since you are overloading the operator acting on that object. Just refer to it as `this`. For this to work though, it needs to be a part of the class, which I believe is another issue. – Easton Bornemeier Jun 22 '17 at 21:46
  • should be part of a class? – Joseph D. Jun 22 '17 at 21:47
  • 1
    @EastonBornemeier No, since `MyClass` is a right argument – alexeykuzmin0 Jun 22 '17 at 21:48
  • 1
    @alexeykuzmin0 That's what I get for trying to resolve a question in a language I haven't worked with in ages ;) – Easton Bornemeier Jun 22 '17 at 21:50
  • Possible duplicate of [declare template friend function of template class](https://stackoverflow.com/questions/18792565/declare-template-friend-function-of-template-class) – isanae Jun 23 '17 at 03:16
  • Also a dupe of [that one](https://stackoverflow.com/questions/3989678/c-template-friend-operator-overloading). – isanae Jun 23 '17 at 03:19

3 Answers3

9

In your code, declarations of friend operators within MyClass should look like these:

template<typename T> friend istream& operator>>(istream&, MyClass<T>&);
template<typename T> friend ostream& operator<<(ostream&, MyClass<T>);

That is, they shall have their own template parameters.

A valid definition for MyClass based on the one provided in the question follows:

template <class MYTYPE>
class MyClass { 
    MYTYPE *myVector;
    int dim;
    string name;
public:
    MyClass(int, string);
    MyClass() {}

    template<typename T>
    friend istream& operator>>(istream&, MyClass<T>&);

    template<typename T>
    friend ostream& operator<<(ostream&, MyClass<T>);
};
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • Thank you for taking your time to write this! However, it still refused to do the trick for me. – NeVada Jun 22 '17 at 22:16
  • 1
    @Marek [It compiles and works just fine](https://wandbox.org/permlink/Bhnayys0oqL0DMjl) doing this. There is a warning due to another part of your code, but this approach solves the issue with the operators at least. – skypjack Jun 22 '17 at 22:26
  • True! Thank you for the solution! Apologies for misunderstanding what you meant. Didn't notice I had that weird indentation there. – NeVada Jun 22 '17 at 22:44
6

Turning up the warning level of the compiler helps. By using -Wall with g++, I get the following warnings before the linker error.

socc.cc:13:58: warning: friend declaration ‘std::istream& operator>>(std::istream&, MyClass<MYTYPE>&                    )’ declares a non-template function [-Wnon-template-friend]
     friend istream& operator>>(istream&, MyClass<MYTYPE>&);
                                                          ^
socc.cc:13:58: note: (if this is not what you intended, make sure the function template has already                     been declared and add <> after the function name here)
socc.cc:14:57: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, MyClass<MYTYPE>                     ’ declares a non-template function [-Wnon-template-friend]
     friend ostream& operator<<(ostream&, MyClass<MYTYPE>);

You need to use function templates for the operator>> and operator<< functions. You can declare them before the definition of the class with:

// Forward the class template.
template <class MYTYPE> class MyClass;

// Declare the function templates.
template <class MYTYPE> 
std::istream& operator>>(std::istream&, MyClass<MYTYPE>&);

template <class MYTYPE> 
std::ostream& operator<<(st::ostream&, MyClass<MYTYPE>);

Then, you'll have to use the friend declaration with the appropriate template parameter.

// This makes sure that operator>><int> is not a friend of MyClass<double>
// Only operator>><double> is a friend of MyClass<double>
friend std::istream& operator>><MYTYPE>(std::istream&, MyClass<MYTYPE>&);
friend std::ostream& operator<<<MYTYPE>(std::ostream&, MyClass<MYTYPE>);

Here's an updated version of your code that builds for me. I haven't tried to run it.

#include <iostream>
#include <string>
using namespace std;

template <class MYTYPE> class MyClass;

template <class MYTYPE> 
std::istream& operator>>(std::istream&, MyClass<MYTYPE>&);

template <class MYTYPE> 
std::ostream& operator<<(std::ostream&, MyClass<MYTYPE>);

template <class MYTYPE>
class MyClass {
    MYTYPE *myVector;
    int dim;
    string name;
public:
    MyClass(int, string);
    MyClass() {};

    friend std::istream& operator>><MYTYPE>(std::istream&, MyClass<MYTYPE>&);
    friend std::ostream& operator<<<MYTYPE>(std::ostream&, MyClass<MYTYPE>);
};

template <class MYTYPE>
MyClass<MYTYPE>::MyClass(int x, string y) {
    dim = x;
    name = y;
    myVector = new MYTYPE[dim];
}


template <class MYTYPE>
std::istream& operator>>(std::istream& X, MyClass<MYTYPE>& a){
    cout<<"Reading vector: "<<a.name<<endl;
    for(int indice = 0; indice < a.dim; indice++){
        cout<<a.name<<'['<<indice<<"]= ";
        X >> a.myVector[indice];
    }
    return X;
}

template <class MYTYPE>
std::ostream& operator<<(std::ostream& X, MyClass<MYTYPE> a){
    X<<"Vector: "<<a.name<<endl;
    for(int indice = 0; indice < a.dim; indice++)
        X<<a.myVector[indice]<<' ';
        X<<endl;
    return X;
}


int main() {
    MyClass<int> object(4, "Ints vector");
    MyClass<string> object2(5, "String vector");
    cin >> object;
    cin >> object2;
    cout << object;
    cout << object2;
    system("pause");
    return 0;
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • It did solve the problem. Oddly enough, I still get this: http://prntscr.com/fn1ixt The program works just fine, but there's still that. – NeVada Jun 22 '17 at 22:13
  • 1
    The friend function declarations within `MyClass` do not need the explicit `MYTYPE` template parameter: `friend std::istream& operator>> <>(std::istream&, MyClass&);`. – isanae Jun 23 '17 at 03:22
  • @isanae, I did not know that. Thanks for the LOTD. – R Sahu Jun 23 '17 at 03:50
3

It is a bit complicated. Actually there are two ways a class template can have a function friend.

For example, take this:

template <typename T>
void fun();

template <typename T>
class A
{
};

Then what do you want?

  1. Or do you want that func<X>() be friend of A<Y> for any other Y?
  2. Do you want that fun<X>() be friend of A<X> but not of A<Y>?

That is, should the function be able to access privater members of any specialization or only to the specialization of the same type.

My guess is that you need option 2. The trick is that you must make friend of the function specialization so the template function must already exist: you have declare the template function first and then make it friend. Something like:

//first declare both function and class
template <typename T>
class A;
template <typename T>
void fun();

//Then define the class
template <typename T>
class A
{
    //A<T> is friend to fun<T>, the specializations must match.
    friend void fun<T>();
};

//And define the function
template <typename T>
void fun()
{
}

If what you want is option 1, then there is no need of the advanced declarations. Just:

template <typename T>
class A
{
    //A<T> is friend to fun<X>, T and X may be different
    template <class X>
    friend void fun();
};

That said, your code is a bit tricky to write because of the << and the >>:

template <class MYTYPE>
class MyClass;

template <class MYTYPE>
istream& operator>>(istream& X, MyClass<MYTYPE>& a);

template <class MYTYPE>
ostream& operator<<(ostream&, MyClass<MYTYPE>);

template <class MYTYPE>
class MyClass {
    // ....
    friend istream& operator>> <MYTYPE>(istream&, MyClass<MYTYPE>&);
    friend ostream& operator<< <MYTYPE>(ostream&, MyClass<MYTYPE>);
};
rodrigo
  • 94,151
  • 12
  • 143
  • 190