关于在类模板中实现友元函数的问题

Veini 2007-09-10 02:28:10
编写一个类模板,在类模板中重载了运算符,并把该函数声明为友元函数.如果在类体外定义该友元函数,则出现如下编译错误:
main.obj : error LNK2019: 无法解析的外部符号 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class SeqList<int> const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$SeqList@H@@@Z),该符号在函数 _main 中被引用
D:\c++练习\SeqList\Debug\SeqList.exe : fatal error LNK1120: 1 个无法解析的外部命令

如果是在类模板中直接定义该友元函数,则能成功编译并运行.这是为什么?
...全文
1572 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
chengmin0823 2011-11-23
  • 打赏
  • 举报
回复
template<class Type>
friend istream& operator>>(istream& in, BiTree <T> &tree);

friend istream& operator>><Type>(istream& out, BiTree <T> &tree);

都可以运行通过
StoNe831120 2008-10-29
  • 打赏
  • 举报
回复
template <class T>
friend std::istream& operator>> (std::istream &in, BiTree <T> &tree);
用类似这样的方法声明试试
wubinhb168 2008-04-23
  • 打赏
  • 举报
回复
那这个链接错误是什么原因呢?
以下是原代码和错误信息。

^^^^^^^^^^^^^^^^^^^^^^^^BiTree.h^^^^^^^^^^^^^^^^^^^^^^^

# ifndef BITREE
# define BITREE

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

// 二叉树练习, BiTreeNode为二叉树节点的结构体

template <class T> class BiTree;

template <class T>
std::istream& operator>> (std::istream& in, BiTree<T> &tree);

template <class T>
std::ostream& operator<< (std::ostream& in, BiTree<T> &tree);


template <class T>
class BiTree
{
public:
BiTree(): root(NULL){} // 构造函数
BiTree(T end): endValue(end), root(NULL){}
~BiTree() { destroy(root); } // 销毁树

private:
BiTreeNode<T> *root; // 树的根节点
T end; // 从控制台输入树节点时的,结束符号
void createBiTree(std::istream& in, BiTreeNode<T>* &subTree); // 构造树
void destroy(BiTreeNode<T>* &subTree); // 销毁树
void traverse(BiTreeNode<T>* &subTree, std::ostream &out)const; // 遍历树

// 输入输出树
friend std::istream& operator>> <T>(std::istream &in, BiTree<T> &tree);
friend std::ostream& operator<< <T>(std::ostream &out, BiTree<T> &tree);
};

template <class T>
std::istream& operator>> <T>(std::istream& in, BiTree<T> &tree)
{
tree.createBiTree(in, tree.root);
return in;
}


template <class T>
std::ostream& operator<< <T>(std::ostream& out, BiTree<T> &tree)
{
out<<"the binary tree node are: "<<endl;
tree.traverse(tree.root, out);
out<<endl;
return out;
}

# endif // binary tree define



^^^^^^^^^^^^^^^^^^^^^^^BiTree.cpp^^^^^^^^^^^^^^^^^^^^^^
# include "BiTree.h"
# include <stack>

using namespace std;


template <class T>
void BiTree<T>::destroy(BiTreeNode<T>* &subTree)
{
if(subTree != NULL)
{
destroy(subTree->left);
destroy(subTree->right);
delete subTree;
}
}


template <class T>
void BiTree<T>::traverse(BiTreeNode<T>* &subTree, ostream& out)const
{
if(subTree != NULL)
{
out<<subTree->data<<' ';
traverse(subTree->left, out);
traverse(subTree->right, out);
}
}


template <class T>
void BiTree<T>::createBiTree(istream &in, BiTreeNode<T>* &bt)
{
stack<BiTreeNode<T> *> s;
BiTreeNode<T> *p, *t;
int k;
bi = NULL;
T ch;
in>>ch;
while(ch != end)
{
switch(ch)
{
case '(':
s.push(p); k = 1; break;

case ')':
s.pop(); break;

case ',':
k = 2; break;

default:
p = new BiTreeNode(ch);
if(bt == NULL)
bt = p;
else if(k == 1)
{
s.top();
t->left = p;
}
else
{
s.top();
t->right = p;
}
}

in>>ch;
}
}

BiTree error LNK2019: 无法解析的外部符号 "private: void __thiscall BiTree<char>::destroy(struct BiTreeNode<char> * &)" (?destroy@?$BiTree@D@@AAEXAAPAU?$BiTreeNode@D@@@Z) ,该符号在函数 "public: __thiscall BiTree<char>::~BiTree<char>(void)" (??1?$BiTree@D@@QAE@XZ) 中被引用

BiTree error LNK2019: 无法解析的外部符号 "private: void __thiscall BiTree<char>::createBiTree(class std::basic_istream<char,struct std::char_traits<char> > &,struct BiTreeNode<char> * &)" (?createBiTree@?$BiTree@D@@AAEXAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAPAU?$BiTreeNode@D@@@Z) ,该符号在函数 "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl operator>><char>(class std::basic_istream<char,struct std::char_traits<char> > &,class BiTree<char> &)" (??$?5D@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$BiTree@D@@@Z) 中被引用

BiTree error LNK2019: 无法解析的外部符号 "private: void __thiscall BiTree<char>::traverse(struct BiTreeNode<char> * &,class std::basic_ostream<char,struct std::char_traits<char> > &)const " (?traverse@?$BiTree@D@@ABEXAAPAU?$BiTreeNode@D@@AAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z) ,该符号在函数 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<<char>(class std::basic_ostream<char,struct std::char_traits<char> > &,class BiTree<char> &)" (??$?6D@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$BiTree@D@@@Z) 中被引用

ryfdizuo 2007-11-28
  • 打赏
  • 举报
回复
更正5楼的回答:
friend ostream& operator <<(ostream&, const SeqList <Type> &);
改为
friend ostream& operator << <Type> (ostream&, const SeqList <Type> &);
-----------------------

模板函数的友元声明
模板在使用之前必须先声明。模板的使用由友元声明构成,不是由模板的声明构成。实际的模板声明必须在友元声明之前。例如,编译系统尝试链接以下示例中生成的目标文件时,对未实例化的 operator<< 函数,会生成未定义错误。


--------------------------------------------------------------------------------

示例 6–2 友元声明问题的示例



array.h
// generates undefined error for the operator<< function
#ifndef ARRAY_H
#define ARRAY_H
#include <iosfwd>

template<class T> class array {
int size;
public:
array();
friend std::ostream&
operator<<(std::ostream&, const array<T>&);
};
#endif

array.cc
#include <stdlib.h>
#include <iostream>

template<class T> array<T>::array() {size = 1024;}

template<class T>
std::ostream&
operator<<(std::ostream& out, const array<T>& rhs)
{return out <<’[’ << rhs.size <<’]’;}

main.cc
#include <iostream>
#include "array.h"

int main()
{
std::cout
<< "creating an array of int... " << std::flush;
array<int> foo;
std::cout << "done\n";
std::cout << foo << std::endl;
return 0;
}

--------------------------------------------------------------------------------

请注意,因为编译器将以下代码作为普通函数(array 类的 friend)的声明进行读取,所以编译期间不会出现错误消息。



friend ostream& operator<<(ostream&, const array<T>&);

因为 operator<< 实际上是模板函数,所以需要在声明 template class array 之前提供模板声明。但是,由于 operator<< 的 type array<T> 的参数,因此必须在声明函数之前声明 array<T> 。文件 array.h 必须如下所示:



#ifndef ARRAY_H
#define ARRAY_H
#include <iosfwd>

// the next two lines declare operator<< as a template function
template<class T> class array;
template<class T>
std::ostream& operator<<(std::ostream&, const array<T>&);

template<class T> class array {
int size;
public:
array();
friend std::ostream&
operator<< <T> (std::ostream&, const array<T>&);
};
#endif
fish6344 2007-11-28
  • 打赏
  • 举报
回复
Veini朋友,从错误提示:

"main.obj : error LNK2019: 无法解析的外部符号 "class std::basic_ostream <char,struct std::char_traits <char> > & __cdecl operator < <(class std::basic_ostream <char,struct std::char_traits <char> > &,class SeqList <int> const &) " (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$SeqList@H@@@Z),该符号在函数 _main 中被引用
D:\c++练习\SeqList\Debug\SeqList.exe : fatal error LNK1120: 1 个无法解析的外部命令 "

来看,生成的链接错误和你的类模板中友员重载运算符无关!而是在你的程序代码中调用了流插入运算符,例如:

std::basic_ostream <char,struct std::char_traits <char> > & __cdecl operator <<(class std::basic_ostream <char,struct std::char_traits <char> > &,class SeqList <int> const &) "

而又没有预包含相关的头文件,例如iostream或导入命名空间,才会生成上述链接误LNK2019!

既然没有你的源代码,我只好猜想,你是否在预期调用你的重载运算符的地方,意外的调用了上述流插入运算符呢?
请仔细检查你的源代码,总之,上述错误与你的类模板中友员重载运算符无关是毫无疑问的!(如果那样,提示名中不会有"std::basic_ostream..."等符号,这些都是与标准流有关的东东!)
xalangying 2007-11-28
  • 打赏
  • 举报
回复

friend ostream& operator <<(ostream&, const SeqList <Type> &);
改为
friend ostream& operator << <>(ostream&, const SeqList <Type> &);
fish6344 2007-09-10
  • 打赏
  • 举报
回复
根据你"在类模板中重载了运算符,并把该函数声明为友元函数.如果在类体外定义该友元函数(外部函数),则出现如下编译错误:
main.obj : error LNK2019: ......"以还导至"LNK1120: 1 个无法解析的外部命令"来看,问是这样:

首先上述错误不是编译期错误,而是链接错误!

其次,可以推断,你使用VC7.1或更高的版本的编译器;

还因为:
你声明并定义了一个重载运算符的友员外部函数如下:

class std::basic_ostream<char,struct std::char_traits<char>>&
__cdecl operator<<(std::basic_ostream<char,struct std::char_traits<char>> &, SeqList<int> const &)"

但你对上述重载的外部运算符函数没有在声明为友员的时候显示声明其模板参数表!

我没有你的源代码,只好演示如下解决之道:

假如有如下Pig类:

template<typename T>
class Pig
{
long x;
};

又有一个模板函数如下:

template<typename T>
void show(Pig<T>& _pg)
{
_pg.x = 8;//修改Pig类的私有成员
cout << "Pig的成员: " << _pg.x << endl; //参阅Pig类的私有成员!
}

则需要如下处理:

template<typename T>class Pig;//预引用类模板,使其对show函数可见!

//这个函数声明(注意,不一定是定义)必须为Pig类可见!
//纯演示用途:
template<typename T>void show(Pig<T>& _pg)
{
_pg.x = 8;//修改Pig类的私有成员
cout << "Pig的成员: " << _pg.x << endl; //参阅Pig类的私有成员!
}

template<typename T>
class Pig
{
long x;
/*
注意,你的函数声明类似于"friend void show(Pig<T>& _pg);"
这正是导至LNK2019链接错误的原因,其后的LNK1120错误源到LNK2019!
必须如下显示声明show的模板参数表<T>,实际上,"show<T>"才是前述模板的id!
*/
friend void show<T>(Pig<T>& _pg);
};

int main()
{
Pig<long> _pg;

show(_pg);//演示完美的友员函数!输出"Pig的成员: 8"

_PAUSE;
return 0;
}


上述程序行为良好,但愿对你有所帮助!
  • 打赏
  • 举报
回复
这样才是正确的写模板的有元函数
template<class T>
class test;
template<class T>
ostream& operator<<(ostream& os,const test<T>& des);

template<class T>
class test
{
T x;
public:
friend ostream& operator<<(ostream& os,const test<T>& des);
};

template<class T>
ostream& operator<<(ostream& os,const test<T>& des)
{
return os<<des.x;
}

int main(int , char* [])
{
test<int> x;
cout<<x<<endl;

return 0;
};
  • 打赏
  • 举报
回复
解析的问题
你写的代码不对,不是在写友元函数,而是重新写了一个函数,所以编译错误。
taodm 2007-09-10
  • 打赏
  • 举报
回复
知道“在类模板中直接定义该友元函数”这个方法就行了。这是最强的通行解,就不要讨论其它解决方法了。

65,174

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧