Search code examples
c++error-handlinglnk2019

unresolved externals- linking issues


currently taking a course for c++ got a project about template classes and functions- got this error msg couldn't tell where from, all files compile just fine. double-checked everything declarations etc

ERROR MSG: Menu.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits > &,class CSet const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$CSet@J@@@Z) referenced in function "private: bool __thiscall Menu::check(void)" (?check@Menu@@AAE_NXZ)

code: CSET// set of type t , header file

#ifndef __C_SET_H__
#define __C_SET_H__
#include <iostream>
using namespace std;

template <class T>
class CSet
{
public:
    CSet() { m_length = 0; m_setArr = NULL; }//ctor
    CSet(const CSet& set);//cctor
    ~CSet() { delete[] m_setArr; }

    //--operators--//
    CSet& operator=(const CSet& );
    CSet& operator+=(const T& );
    CSet& operator-=(const T& );
     CSet& operator-(const CSet& )const;

    friend ostream& operator<<(ostream&, const CSet& );//ouput

    //--methods--//
    const CSet UNION(const CSet& );
    const CSet INTERSECT(const CSet& );

private:
    int m_length;
    T* m_setArr;

     int appears(const T& );//private method check index of T in setArr


};



template <class T>
CSet<T>::CSet(const CSet& set)//cctor
{
    *this = set;
}

template <class T>
CSet<T>& CSet<T>::operator=(const CSet& set)
{
    m_length = set.m_length;
    m_setArr = set.m_setArr;
    return *this;
}

template <class T>
CSet<T>& CSet<T>::operator+=(const T& val)
{
    if (appears(val) == -1)
        return *this;
    try
    {
        T* arr = new T[m_length + 1];
        for (int i = 0; i < m_length; i++)
            arr[i] = m_setArr[i];
        arr[m_length] = val;
        m_length++;
        delete[] m_setArr;
        m_setArr = arr;
        return *this;
    }
    catch (bad_alloc)
    {
        cout << "Memory Allocation Failed!" << endl;
        return *this;
    }
    
}

template <class T>
CSet<T>& CSet<T>::operator-=(const T& val)
{
    int index = appears(val);
    if (index == -1)
        return *this;
    m_length--;
    try
    {
        T* arr = new T[m_length];
        int j = 0;//index
        for (int i = 0; i < m_length; i++)
        {
            if (i == index)
                continue;
            arr[j] = m_setArr[i];
            j++;
        }
        delete[] m_setArr;
        m_setArr = arr;
        return *this;
    }
    catch (bad_alloc)
    {
        cout << "Memory Allocation Failed!" << endl;
        return *this;
    }
}

template <class T>
 CSet<T>& CSet<T>::operator-(const CSet& set)const
{

    CSet copy;
    copy.m_length = m_length;
    copy.m_setArr=m_setArr;
    copy.INTERSECT(set);
    CSet copy2=set;
    for (int i = 0; i < copy.m_length; i++)
        copy2.operator-=(copy.m_setArr[i]);
    return copy2;
}

template <class T>
ostream& operator<<(ostream& os, const CSet<T>& set)
{
    if (set.m_length == 0)
        os << "The set is empty!" << endl;
    else{
        os << '(';
        int i;
        for (i = 0; i < set.m_length - 1; i++)
            os << set.m_setArr[i] << ',';
        os << set.m_setArr[i] << ')' << endl;
    }
    return os;
}

template <class T>
const CSet<T> CSet<T>::UNION(const CSet& set)
{
    CSet copy;
    copy.m_length = m_length;
    copy.m_setArr = m_setArr;
    for (int i = 0; i < set.m_length; i++)
        copy.operator+=(set.m_setArr[i]);
    return copy;
}

template <class T>
const CSet<T> CSet<T>::INTERSECT(const CSet& set)//private
{
    CSet copy;
    copy.m_length = m_length;
    copy.m_setArr = m_setArr;
    for (int i = 0; i < set.m_length; i++)
        copy.operator-=(set.m_setArr[i]);
    return copy;
}

template <class T>
 int CSet<T>::appears(const T& val)//private
{
    for (int i = 0; i < m_length; i++)
        if (m_setArr[i] == val)
            return i;
    return -1;

}

#endif

MENU//prints and show menu for sets H FILE

#ifndef __MENU_H__
#define __MENU_H__
#include"CSet.h"
#include <iostream>
using namespace std;
class Menu
{
public:
    Menu();//prints menu
private:
    //--members--//
    CSet<long> longSet;
    CSet<char> chSet1;
    CSet<char> chSet2;
    CSet<string> strSet;
    //--methods--//
    bool check();
    void printSetsOp();
    void addRemoveElement(bool);
    void difference();

};
#endif

MENU.CPP

#include "Menu.h"
#include "CSet.h"
#include <iostream>
using namespace std;

Menu::Menu()
{//menu print
    do {
        //show the menu until the user wants to exit
        cout << "================MENU================" << endl;
        ...
    } while (check());//as long as the user doesnt want to exit
}

bool Menu::check()
{
    int choice;
    cin >> choice;//user input
    switch (choice)
    {
    case 1:
    {
        ...
        return true;//keep the menu loop going
    }
    .
    .
    .
    case 7:
    {//exit
        cout << "Goodbye!" << endl;
        return false;//exit the program
    }
    default:
    {//invalid number
        cout << "please enter a valid number!" << endl;
        return true;//keep the menu loop going
    }
    }
}

void Menu::printSetsOp()
{
    //print options
}

void Menu::addRemoveElement(bool add)
{
    printSetsOp();//print scnd menu
    int choice;
    cin >> choice;//which set
    switch (choice)
    {
    case 1:
    {//add or remove elements, each type seperatly
        long elm;
        cout << "Insert number =  ";
        cin >> elm;
        add ? longSet.operator+=(elm) : longSet.operator-=(elm);
        return;
    }
    case 2:
    case 3:
    {
        char ch;
        cout << "Insert character =  ";
        cin >> ch;
        if (add)
            (choice == 2) ? chSet1.operator+=(ch) : chSet2.operator+=(ch);
        else
            (choice == 2) ? chSet1.operator-=(ch) : chSet2.operator-=(ch);
        return;
    }
    default:
    {//invalid number
        cout << "please enter a valid number!" << endl;
        return;//keep the menu loop going
    }
    }
}

don't know what causes this error so posted pretty much everything. THANK YOU


Solution

  • The problem is that your CSet's friend operator<< names a different function than your template operator<<. This actually declares a new non-template function which winds up being a better candidate than the template function, but then no such function is ever defined, hence the linking error.

    You can fix this by making the friend declaration refer to the full specialization of this operator for a specific CSet<T>. To do this, the template function must already be declared, which requires forward-declaration of CSet. So, you'd do something like this:

    template <class T> class CSet;
    
    template <class T>
    ostream& operator<<(ostream& os, const CSet<T>& set)
    {
        // implementation here
    }
    
    template <class T>
    class CSet {
        // all of your other stuff...
    
        // Add <> to have the friend declaration refer to a specific operator<< instantiation
        // With <>, T is deduced. You can also say <T> instead.
        friend ostream& operator<< <>(ostream&, const CSet& );
    };
    

    Note that most compilers should warn you when you declare a non-template friend function in a template type, because it leads to exactly the problem you're having. Make sure that your compiler warnings are enabled.

    warning: friend declaration 'std::ostream& operator<<(std::ostream&, const foo<T>&)' declares a non-template function