简体   繁体   中英

Overloading istream>> and ostream<< in a templated class

I want to overload >> and << in a templated class and I'm getting some errors.

Severity    Code    Description Project File    Line    Suppression State
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 MatricePatratica<int> &)" (??5@YAAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$MatricePatratica@H@@@Z) referenced in function _main   ConsoleApplication3 C:\Users\Octavian\source\repos\ConsoleApplication3\ConsoleApplication3\Source.obj   1   
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 MatricePatratica<int> const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$MatricePatratica@H@@@Z) referenced in function _main ConsoleApplication3 C:\Users\Octavian\source\repos\ConsoleApplication3\ConsoleApplication3\Source.obj   1   
Error   LNK1120 2 unresolved externals  ConsoleApplication3 C:\Users\Octavian\source\repos\ConsoleApplication3\Debug\ConsoleApplication3.exe    1   

AFAIK this happens when a function is declared but not implemented. I get those errors for overloading >> << (as you can see above)

The only thing is that I implemented those functions so I expected it to work. But I might be missing something.

Here's my templated class

#pragma once

template <typename T>
class MatricePatratica
{
private:
    T** date;
    int n = 0;
public:
    MatricePatratica();
    MatricePatratica(int);
    MatricePatratica(const MatricePatratica&);

    friend std::istream& operator>>(std::istream& is, MatricePatratica<T>& obj);
    friend std::ostream& operator<<(std::ostream& os, const MatricePatratica<T>& obj);
};

template<typename T>
inline MatricePatratica<T>::MatricePatratica()
{
}

template<typename T>
inline MatricePatratica<T>::MatricePatratica(int n) :n(n)
{
    date = new int* [n];
    for (int i = 0; i < n; i++)
    {
        date[i] = new int[n];
    }
}

template<typename T>
inline MatricePatratica<T>::MatricePatratica(const MatricePatratica& deCopiat)
{
    date = new int* [deCopiat->n];
    for (int i = 0; i < deCopiat->n; i++)
    {
        date[i] = new int[deCopiat->n];
    }
    for (int i = 0; i < deCopiat->n; i++)
    {
        for (int j = 0; j < deCopiat->n; j++)
        {
            date[i][j] = deCopiat[i][j];
        }
    }
}

template <typename T>
std::istream& operator>>(std::istream& is, MatricePatratica<T>& obj)
{
    for (int i = 0; i < obj.n; i++)
    {
        for (int j = 0; j < obj.n; j++)
        {
            is >> obj.date[i][j];
        }
    }
    return is;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const MatricePatratica<T>& obj)
{
    for (int i = 0; i < obj.n; i++)
    {
        for (int j = 0; j < obj.n; j++)
        {
            os << obj[i][j] << " ";
        }
        os << std::endl;
    }
    return os;
}

and here's the main file:

#include <iostream>
#include "MatricePatratica.h"

int main()
{
    MatricePatratica<int> test(5);
    std::cin >> test;
    std::cout << test;
}

Why am I getting this error and how should I fix it?

Live demo

Problem is that you have declared a friendship to something what is not a template!

So compiler tries to find:

std::istream& operator>>(std::istream& is, MatricePatratica<int>& obj)

Which doesn't exists, when in fact it should use:

// note extra template parameter:
std::istream& operator>><int>(std::istream& is, MatricePatratica<int>& obj)

To fix it you need declare friendship to a template of those operators:

template <typename T>
class MatricePatratica
{
....
    template<typename U>
    friend std::istream& operator>>(std::istream& is, MatricePatratica<U>& obj);
    
    template<typename U>
    friend std::ostream& operator<<(std::ostream& os, const MatricePatratica<U>& obj);
};

After fixing typo it works: https://wandbox.org/permlink/gHEmap8zCCTyBjRA

Here's an alternate method of getting the friend functions to work. There's a bit more code, but it has the perk of tying the friend function instantiations to those of the class.

The issue with the accepted answer is that it is too free. Say you have a MatricePatratica<int> . It has as friends all possible versions of your operator<<() and operator>>() ; as in operator<< <double>() is a friend of your MatricePatratica<int> and the version for <std::string> and so on. It becomes possible to attempt to use a friend function that isn't compatible with the class type.

The version of your header below fixes that. My reference is this link , and it contains a concrete example of how the accepted answer can bite you.

#pragma once

#include <iostream>  // CHANGE: Include what you use

// CHANGE: Forward declare class and friends
template <typename T>
class MatricePatratica;

template <typename T>
std::istream& operator>>(std::istream& is, MatricePatratica<T>& obj);

template <typename T>
std::ostream& operator<<(std::ostream& os, const MatricePatratica<T>& obj);

template <typename T>
class MatricePatratica
{
private:
    T** date;
    int n = 0;
public:
    MatricePatratica();
    MatricePatratica(int);
    MatricePatratica(const MatricePatratica&);

    // Note movement of <T>
    friend std::istream& operator>><T>(std::istream& is, MatricePatratica& obj);
    friend std::ostream& operator<<<T>(std::ostream& os, const MatricePatratica& obj);
};

template<typename T>
inline MatricePatratica<T>::MatricePatratica()
{
}

template<typename T>
inline MatricePatratica<T>::MatricePatratica(int n) :n(n)
{
    date = new int* [n];
    for (int i = 0; i < n; i++)
    {
        date[i] = new int[n];
    }
}

template<typename T>
inline MatricePatratica<T>::MatricePatratica(const MatricePatratica& deCopiat)
{
    date = new int* [deCopiat->n];
    for (int i = 0; i < deCopiat->n; i++)
    {
        date[i] = new int[deCopiat->n];
    }
    for (int i = 0; i < deCopiat->n; i++)
    {
        for (int j = 0; j < deCopiat->n; j++)
        {
            date[i][j] = deCopiat[i][j];
        }
    }
}

template <typename T>
std::istream& operator>>(std::istream& is, MatricePatratica<T>& obj)
{
    for (int i = 0; i < obj.n; i++)
    {
        for (int j = 0; j < obj.n; j++)
        {
            is >> obj.date[i][j];
        }
    }
    return is;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const MatricePatratica<T>& obj)
{
    for (int i = 0; i < obj.n; i++)
    {
        for (int j = 0; j < obj.n; j++)
        {
            os << "TEST" /*obj[i][j]*/ << " ";  // CHANGE: Due to compilation error
        }
        os << '\n';  // CHANGE: Best to avoid baking in obvious inefficiences
    }
    return os;
}

Now, per my reference, this approach is also not without its downside. There are cases where the template may not make an expected implicit conversion.

The surest way to get a friend function to behave completely as expected in a template class is to implement it inline (I mean in the class declaration). I don't believe your class will hit that edge case, and the method above is usually what I do.

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