C++模板类fractionType的LNK2019未解析外部符号错误求助
问题:模板化fractionType类的LNK2019链接错误
我正在为作业实现模板化的fractionType类,将原cpp文件中无法模板化的代码移到header文件后,解决了大部分问题,但仍遗留两个LNK2019错误:提示ostream的operator<<和istream的operator>>为未解析外部符号,在main函数中被引用。若在fractiontype.h中包含main.cpp会引发大量如fractionType未声明等错误。
现有代码
main.cpp
#include <iostream> #include "fractiontype.h" using namespace std; int main() { //Fraction1 and Fraction2 Objects fractionType<float> fraction1(2, 3); fractionType<float> fraction2(0, 0); //Grab first fraction from user cout << "Enter a fraction in the form a/b: "; cin >> fraction1; //Grab second fraction from user cout << "Enter another fraction in the form a/b: "; cin >> fraction2; //Operators cout << fraction1 << " + " << fraction2 << " = " << fraction1 + fraction2 << "\n"; cout << fraction1 << " - " << fraction2 << " = " << fraction1 - fraction2 << "\n"; cout << fraction1 << " * " << fraction2 << " = " << fraction1 * fraction2 << "\n"; cout << fraction1 << " / " << fraction2 << " = " << fraction1 / fraction2 << "\n"; //Checking true or false statements cout << "\n" << fraction1 << " == " << fraction2; if (fraction1 == fraction2) { cout << " is true. \n"; } else { cout << " is false. \n"; } cout << fraction1 << " != " << fraction2; if (fraction1 != fraction2) { cout << " is true. \n"; } else { cout << " is false. \n"; } cout << fraction1 << " < " << fraction2; if (fraction1 < fraction2) { cout << " is true. \n"; } else { cout << " is false. \n"; } cout << fraction1 << " <= " << fraction2; if (fraction1 <= fraction2) { cout << " is true. \n"; } else { cout << " is false. \n"; } cout << fraction1 << " > " << fraction2; if (fraction1 > fraction2) { cout << " is true. \n"; } else { cout << " is false. \n"; } cout << fraction1 << " >= " << fraction2; if (fraction1 >= fraction2) { cout << " is true. \n"; } else { cout << " is false. \n"; } return 0; }
fractiontype.h
#ifndef H_fraction #define H_fraction #include <iostream> using namespace std; template <class T> class fractionType { //overload the stream insertion and extraction operators friend ostream& operator<<(ostream&, const fractionType<T>&); friend istream& operator>>(istream&, fractionType<T>&); public: //overload the assignment operator const fractionType<T>& operator=(const fractionType<T>&); //constructors fractionType(); fractionType(const fractionType<T>&); fractionType(const float&, const float&); //desctructor ~fractionType(); //oveload the relational operators bool operator==(const fractionType<T>&) const; bool operator!=(const fractionType<T>&) const; bool operator<=(const fractionType<T>&) const; bool operator<(const fractionType<T>&) const; bool operator>=(const fractionType<T>&) const; bool operator>(const fractionType<T>&) const; //overload the arithemetic operators fractionType<T> operator+(const fractionType<T>&); fractionType<T> operator-(const fractionType<T>&); fractionType<T> operator*(const fractionType<T>&); fractionType<T> operator/(const fractionType<T>&); private: T numenator; //variable to store the numerator T denominator; //variable to store the denominator }; template <class T> fractionType<T>::fractionType(const float& nu, const float& de) { numenator = nu; if (de == 0) { cout << "\n\tInvalid demoninator. " << "Default value considered for denominator. "; denominator = 1; } else { denominator = de; } }//end function fractionType // Default constructor template <class T> fractionType<T>::fractionType() { numenator = 0; denominator = 1; } // copy constructor template <class T> fractionType<T>::fractionType(const fractionType<T>& rightFraction) { numenator = rightFraction.numenator; denominator = rightFraction.denominator; }//end copy constructor //Desctructor template <class T> fractionType<T>::~fractionType() { }//end Desctructor //oveload the relational operators template <class T> bool fractionType<T>::operator==(const fractionType<T>& rightFraction) const { return ((numenator == rightFraction.numenator) && (denominator == rightFraction.denominator)); }//end function operator== template <class T> bool fractionType<T>::operator!=(const fractionType<T>& rightFraction) const { return ((numenator != rightFraction.numenator) || (denominator != rightFraction.denominator)); }//end function operator!= template <class T> bool fractionType<T>::operator<(const fractionType<T>& rightFraction) const { return ((numenator * rightFraction.denominator) < (denominator * rightFraction.numenator)); }//end function operator< template <class T> bool fractionType<T>::operator<=(const fractionType<T>& rightFraction) const { return ((numenator * rightFraction.denominator) <= (denominator * rightFraction.numenator)); }//end function operator<= template <class T> bool fractionType<T>::operator>(const fractionType<T>& rightFraction) const { return ((numenator * rightFraction.denominator) > (denominator * rightFraction.numenator)); }//end function operator> template <class T> bool fractionType<T>::operator>=(const fractionType<T>& rightFraction) const { return ((numenator * rightFraction.denominator) >= (denominator * rightFraction.numenator)); }//end function operator>= //overload the arithemetic operators template <class T> fractionType<T> fractionType<T>::operator+(const fractionType<T>& rightFraction) { fractionType fractionHolder; fractionHolder.numenator = (numenator * rightFraction.denominator) + (denominator * rightFraction.numenator); fractionHolder.denominator = denominator * rightFraction.denominator; return fractionHolder; }//end function operator+ template <class T> fractionType<T> fractionType<T>::operator-(const fractionType<T>& rightFraction) { fractionType fractionHolder; fractionHolder.numenator = (numenator * rightFraction.denominator) - (denominator * rightFraction.numenator); fractionHolder.denominator = denominator * rightFraction.denominator; return fractionHolder; }//end function operator- template <class T> fractionType<T> fractionType<T>::operator*(const fractionType<T>& rightFraction) { fractionType fractionHolder; fractionHolder.numenator = numenator * rightFraction.numenator; fractionHolder.denominator = denominator * rightFraction.denominator; return fractionHolder; }//end function operator* template <class T> fractionType<T> fractionType<T>::operator/(const fractionType<T>& rightFraction) { fractionType fractionHolder; //check for validity for division if (rightFraction.numenator == 0 || rightFraction.denominator == 0) { fractionHolder.numenator = 0; fractionHolder.denominator = 1; cout << "Invalid to perform division."; } else { fractionHolder.numenator = numenator * rightFraction.denominator; fractionHolder.denominator = denominator * rightFraction.numenator; }//end else - if x return fractionHolder; }//end function operator+ //overload the stream extraction operators template <class T> ostream& operator<<(ostream& osObject, const fractionType<T>& myFraction) { osObject << myFraction.numenator << "/" << myFraction.denominator; return osObject; }//end function operator<< template <class T> istream& operator>>(istream& isObject, fractionType<T>& myFraction) { char ch; isObject >> myFraction.numenator >> ch >> myFraction.denominator; return isObject; }//end function operator>> #endif
问题原因
当前代码中,模板类fractionType的友元运算符operator<<和operator>>被声明为非模板函数,但实际定义的是模板版本。编译器会认为这是两组不同的函数,导致链接时找不到对应函数的实例化代码,从而抛出LNK2019错误。
解决方案
需要将友元运算符明确声明为模板函数,让编译器关联声明和定义。具体修改如下:
- 在类声明前,提前声明模板类和两个友元运算符;
- 在类内部的友元声明中,指定模板参数。
修正后的fractiontype.h
#ifndef H_fraction #define H_fraction #include <iostream> using namespace std; // 提前声明模板类和友元运算符 template <class T> class fractionType; template <class T> ostream& operator<<(ostream&, const fractionType<T>&); template <class T> istream& operator>>(istream&, fractionType<T>&); template <class T> class fractionType { // 明确声明友元为对应模板参数的函数 friend ostream& operator<< <T>(ostream&, const fractionType<T>&); friend istream& operator>> <T>(istream&, fractionType<T>&); public: const fractionType<T>& operator=(const fractionType<T>&); fractionType(); fractionType(const fractionType<T>&); fractionType(const T&, const T&); // 将参数类型改为T,保持模板一致性 ~fractionType(); bool operator==(const fractionType<T>&) const; bool operator!=(const fractionType<T>&) const; bool operator<=(const fractionType<T>&) const; bool operator<(const fractionType<T>&) const; bool operator>=(const fractionType<T>&) const; bool operator>(const fractionType<T>&) const; fractionType<T> operator+(const fractionType<T>&); fractionType<T> operator-(const fractionType<T>&); fractionType<T> operator*(const fractionType<T>&); fractionType<T> operator/(const fractionType<T>&); private: T numenator; T denominator; }; template <class T> fractionType<T>::fractionType(const T& nu, const T& de) { // 参数类型改为T numenator = nu; if (de == 0) { cout << "\n\tInvalid denominator. Default value considered for denominator. "; denominator = 1; } else { denominator = de; } } template <class T> fractionType<T>::fractionType() { numenator = 0; denominator = 1; } template <class T> fractionType<T>::fractionType(const fractionType<T>& rightFraction) { numenator = rightFraction.numenator; denominator = rightFraction.denominator; } template <class T> fractionType<T>::~fractionType() {} template <class T> const fractionType<T>& fractionType<T>::operator=(const fractionType<T>& rightFraction) { // 补充赋值运算符实现 if (this != &rightFraction) { numenator = rightFraction.numenator; denominator = rightFraction.denominator; } return *this; } template <class T> bool fractionType<T>::operator==(const fractionType<T>& rightFraction) const { return ((numenator == rightFraction.numenator) && (denominator == rightFraction.denominator)); } template <class T> bool fractionType<T>::operator!=(const fractionType<T>& rightFraction) const { return !(*this == rightFraction); // 复用==运算符,减少冗余 } template <class T> bool fractionType<T>::operator<(const fractionType<T>& rightFraction) const { return ((numenator * rightFraction.denominator) < (denominator * rightFraction.numenator)); } template <class T> bool fractionType<T>::operator<=(const fractionType<T>& rightFraction) const { return (*this < rightFraction) || (*this == rightFraction); // 复用<和==运算符 } template <class T> bool fractionType<T>::operator>(const fractionType<T>& rightFraction) const { return !(*this <= rightFraction); // 复用<=运算符 } template <class T> bool fractionType<T>::operator>=(const fractionType<T>& rightFraction) const { return !(*this < rightFraction); // 复用<运算符 } template <class T> fractionType<T> fractionType<T>::operator+(const fractionType<T>& rightFraction) { fractionType fractionHolder; fractionHolder.numenator = (numenator * rightFraction.denominator) + (denominator * rightFraction.numenator); fractionHolder.denominator = denominator * rightFraction.denominator; return fractionHolder; } template <class T> fractionType<T> fractionType<T>::operator-(const fractionType<T>& rightFraction) { fractionType fractionHolder; fractionHolder.numenator = (numenator * rightFraction.denominator) - (denominator * rightFraction.numenator); fractionHolder.denominator = denominator * rightFraction.denominator; return fractionHolder; } template <class T> fractionType<T> fractionType<T>::operator*(const fractionType<T>& rightFraction) { fractionType fractionHolder; fractionHolder.numenator = numenator * rightFraction.numenator; fractionHolder.denominator = denominator * rightFraction.denominator; return fractionHolder; } template <class T> fractionType<T> fractionType<T>::operator/(const fractionType<T>& rightFraction) { fractionType fractionHolder; if (rightFraction.numenator == 0 || rightFraction.denominator == 0) { fractionHolder.numenator = 0; fractionHolder.denominator = 1; cout << "Invalid to perform division."; } else { fractionHolder.numenator = numenator * rightFraction.denominator; fractionHolder.denominator = denominator * rightFraction.numenator; } return fractionHolder; } template <class T> ostream& operator<<(ostream& osObject, const fractionType<T>& myFraction) { osObject << myFraction.numenator << "/" << myFraction.denominator; return osObject; } template <class T> istream& operator>>(istream& isObject, fractionType<T>& myFraction) { char ch; isObject >> myFraction.numenator >> ch >> myFraction.denominator; return isObject; } #endif
额外优化说明
- 把构造函数的参数类型从
float改为模板参数T,保证模板类的通用性; - 补充了之前缺失的赋值运算符
operator=实现; - 复用已实现的运算符简化其他关系运算符代码,减少冗余和出错概率;
- 修正拼写错误:
demoninator改为denominator。
内容的提问来源于stack exchange,提问作者2Silent4You
相关产品推荐
相关产品推荐

