【问题标题】:unresolved externals- linking issues未解决的外部链接问题
【发布时间】:2020-10-15 09:43:16
【问题描述】:

目前正在学习 c++ 课程,有一个关于模板类和函数的项目- 得到这个错误消息无法告诉从哪里,所有文件都编译得很好。 仔细检查所有声明等

错误信息: Menu.obj : 错误 LNK2019: 无法解析的外部符号 "class std::basic_ostream & __cdecl operator &,class CSet const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$CSet@J@@@Z) 在函数 "private: bool __thiscall Menu: :check(void)" (?check@Menu@@AAE_NXZ)

代码: CSET// 类型 t 的集合,头文件

#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//打印并显示设置菜单 文件

#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

菜单.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
    }
    }
}

不知道是什么导致了这个错误,所以发布了几乎所有内容。 谢谢你

【问题讨论】:

  • 看起来你实际上并没有在任何地方实现CSet 成员,或者至少你没有在问题中包含它。
  • @cdhowie 它的菜单成员
  • @drescherjm 抱歉已修复,谢谢

标签: c++ error-handling lnk2019


【解决方案1】:

问题是您CSet 的朋友operator&lt;&lt; 命名的函数与您的模板operator&lt;&lt; 不同。这实际上声明了一个新的 non-template 函数,它最终成为比模板函数更好的候选者,但是没有定义过这样的函数,因此出现了链接错误。

您可以通过使朋友声明引用此运算符对特定CSet&lt;T&gt; 的完整特化来解决此问题。为此,必须已经声明了模板函数,这需要前向声明CSet。所以,你会做这样的事情:

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

请注意,当您在模板类型中声明非模板友元函数时,大多数编译器都会警告您,因为它会导致您遇到的问题。确保您的编译器警告已启用。

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

【讨论】:

  • 所以我必须对我需要的每种类型进行专业化? 'const CSet& set ' T 这里是实型? @cdhowie
  • @Abby 不,这一切只是让操作员的特定专业化成为朋友。也就是说,operator&lt;&lt; &lt;int&gt;(ostream&amp;, const CSet&lt;int&gt; &amp;)CSet&lt;int&gt; 的朋友,但 operator&lt;&lt; &lt;float&gt;(ostream&amp;, const CSet&lt;float&gt; &amp;)不是朋友。
  • @Abby 我不完全确定你在问什么。