Code Bye

友元函数利用模板重载函数定义

错误:E:\Qt Project\test02\main.cpp:3048: error: undefined reference to `listsavitch::operator<<(std::ostream&, listsavitch::GenericList<int> const&)”
E:\Qt Project\test02\main.cpp:3055: error: undefined reference to `listsavitch::operator<<(std::ostream&, listsavitch::GenericList<char> const&)”
这种错误该怎么解决呢?友元函数是不能定义模板吗?还有一个警告,说是在头文件定义的不是一个模板函数。
//接口文件   genericlist.h
#ifndef GENERICLIST_H
#define GENERICLIST_H
#include <iostream>
using namespace std;
namespace listsavitch
{
  template <class ItemType>
  class GenericList
  {
  public:
    GenericList(int max);
    ~GenericList();
    int length() const;
    bool full() const;
    void add(ItemType new_item);
    void erase();    
    friend ostream& operator<<(ostream& outs, const GenericList<ItemType>& the_list);   //问题出现在这里
  private:
    ItemType *item;
    int max_length;
    int current_length;
  };
}   //end listsavitch
#endif // GENERICLIST_H
#ifndef GENERICLIST_CPP
#define GENERICLIST_CPP
//实现文件 genericlist.cpp
#include <iostream>
#include <cstdlib>
#include "genericlist.h"
using namespace std;
namespace listsavitch
{
  template <class ItemType>
  GenericList<ItemType>::GenericList(int max)
    :max_length(max), current_length(0)
  {
    item = new ItemType[max];
  }
  template <class ItemType>
  GenericList<ItemType>::~GenericList()
  {
    delete [] item;
  }
  template <class ItemType>
   int GenericList<ItemType>:: length() const
   {
     return current_length;
   }
   template <class ItemType>
   bool GenericList<ItemType>:: full() const
   {
     return (max_length == current_length);
   }
   template <class ItemType>
   void GenericList<ItemType>:: add(ItemType new_item)
   {
     if(full())
       {
         cout << "Error: Adding to a full list.\n";
         exit(1);
       }
     else
       {
         item[current_length] = new_item;
         current_length++;
       }
   }
   template <class ItemType>
   void GenericList<ItemType>:: erase()
   {
     current_length = 0;
   }
   template <class ItemType>
   ostream& operator <<(ostream& outs,const GenericList<ItemType>& the_list)  //问题出现在这里
   {
     for (int i = 0; i < the_list.current_length; i++)
       outs << the_list.item[i] << endl;
     return outs;
   }
}
#endif  //GENERICLIST_CPP
//应用程序  main.cpp
#include <iostream>
#include "genericlist.h"
#include "genericlist.cpp"
using namespace std;
using namespace listsavitch;
int main ()
{
  GenericList<int> first_list(2);
  first_list.add(3);
  first_list.add(4);
  cout << "first_list = \n";
  cout << first_list ;      //问题出现在这里,重载了操作符<<,但是没办法定义函数GenericList<int> & the_list
  GenericList<char> second_list(10);
  second_list.add("a");
  second_list.add("b");
  second_list.add("c");
  cout << "second_list = \n";
  cout << second_list;
  return 0;
}
解决方案

5

解决办法:
genericlist.h中修改如下:
friend ostream& operator<< <ItemType>(ostream& outs, const GenericList<ItemType>& the_list);
模板是两次编译生成的,第一次生成的函数头和第二次生成的函数头不一样则报错。
只有在重载<<, >> 时,才使用友元函数,其他情况友元+模板,很复杂,禁止滥用友元函数。

10

本人记得有三个办法
1)不在外部定义友元函数,友元函数定义在 类的内部,原因是友元是类的接口。
这种方式最简洁。
template <typename ItemType>
class GenericList{
friend ostream& operator<<(ostream& outs, const GenericList& the_list){
     // ....
    return os;
};
};

2)类模板内部,声明友元模板函数,类外定义
这样唯一的缺陷是,全部友元模板函数,都是每个模板类的友元函数。
形成多对多友元,而不是一对一友元,
应用上没问题,逻辑上有问题
代码也比较简单,不如第一种方法简洁

template <typename T>
class GenericList{
//友元声明为模板函数
template <U>
friend ostream& operator<<(ostream& outs, const GenericList<U>& the_list);
};
template < ItemType>
friend ostream& operator<<(ostream& outs, const GenericList< ItemType>& the_list){
     // ....
    return os;
};

3)类内声明友元,类外实现,
需要 类模板GenericList 和operator<<  都前置声明
代码最复杂,不过满足了两个要求
3.1)类定义,和实现代码分离//仅仅是代码分离,不能分离编译
3.2)友元函数和模板类,是一对一关系,一个友元函数,对应一个模板类,逻辑正确。

// a)模板类类前置声明:
  template <typename ItemType>    class GenericList;
//b)模板函数  operator<<   前置声明 :
  template <typename U>
    ostream& operator<< (ostream& outs, const GenericList<U>& the_list);
//类定义 
 template <typename ItemType>
  class GenericList
  {
  public:
    ///  。
//c)    友元函数是模板函数的特化,在模板类中,这么写:
//注意 friend ostream& operator<< <>
//(ostream& outs, 
//const GenericList& the_list); 注意GenericList& 
     
friend ostream& operator<< <>(ostream& outs, const GenericList& the_list);
 };
//类的实现部分:可分离编写
//d) 友元函数实现代码
 template <class ItemType>
   ostream& operator <<(ostream& outs,const GenericList<ItemType>& the_list)  //没问题
   {
     for (int i = 0; i < the_list.current_length; i++)
       outs << the_list.item[i] << endl;
     return outs;
   }

5

引用:

虽然分开写了,但是本人用了#include预编译包括了文件啊!这种方式应该是可以的吧?书上是有说过模板不能独立编译,最好是和要调用的程序放在一个文件下,并在调用函数之前。

你非要这样写,那就点开本人给的链接,里面写的很清楚,关于产生这种问题的原因和解决方案。
本人的意思是,既然不能分开编译,那么写的时候干脆合并在一起就好了,就像那个链接最后那段代码一样,反而会方便一些。


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明友元函数利用模板重载函数定义