none
为模板类重载operator<<时出的错误 RRS feed

  • 问题

  • #pragma once
    #include <iostream>
    
    template<typename T>
    class B;
    
    template<typename T>
    std::ostream& operator<<(std::ostream &o, const B<T> &b)
    {
    	o<<b.b<<std::endl;
    	return o;
    }
    
    template<typename T>
    class B
    {
    	friend std::ostream& operator<<(std::ostream &, const B<T> &);
    public:
    	explicit B(int _b = 0) : b(_b)
    	{
    	}
    private:
    	int b;
    };

    以上是test.h文件,文件main.cpp是这样的

    #include <iostream>
    #include "test.h"
    using namespace std;
    
    int main()
    {
    	B<int> b(2);
    
    	cout<<b;
    	return 0;
    }
    

    请问,为什么链接时会出现错误:

    1>main.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class B<int> const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$B@H@@@Z) referenced in function _main
    1>C:\Users\lhl\Documents\Visual Studio 2008\Projects\lhlproj\Debug\test2.exe : fatal error LNK1120: 1 unresolved externals

    如果友元声明是这样:

    template<typename _T>
    friend std::ostream& operator<<(std::ostream &, const B<_T> &);

    就可以链接通过。为什么?


    LHL
    2010年4月1日 6:40

答案

  • template
    <typename
     T>
    std::ostream& operator <<(std::ostream &o, const B<T> &b)

    只是一个模板函数。

    类型T,与

    template
    <typename
     T>
    class B

    中的类型T没有关系。

    在声明友元时

    friend
     std::ostream& operator
    <<(std::ostream &, const
     B<T> &);

    并不能将这个有元函数理解为函数的一部分。

     


    麻烦把正确答案设为解答。
    • 已标记为答案 lhlzhxh 2010年4月14日 3:06
    2010年4月2日 1:16
    版主
  • Hi lhlzhxh.

    如果友元声明是这样:

    template<typename _T>
    friend std::ostream& operator<<(std::ostream &, const B<_T> &);
    
    在我机器上测试OK,能够正常编译及输出。我的编译器是VS2008.
    请:
    1.确定你的编译器是否支持这样的模板语法,能否告知你的编译器类型。
    2.若编译器没有问题,请clean,然后rebuild all 试一试。
    ------
    Bob.Shao
    • 已标记为答案 lhlzhxh 2010年4月14日 3:06
    2010年4月13日 13:59
  • 前面已经说过了,函数模板和类模板特化不是一回事。你的友元函数声明特化后成为

     

    ostream & operator << <int> (std::ostream &, const B<int> )

    而不是你前面声明的

    template<T>

    ostream & operator << (std::ostream &, const B<T> )

    在C++里面通过模板特化的函数和没有通过模板特化的函数是不同的。例如同时声明两个函数

    template<typename T> void f(T);

    void f(int);

    f(1)调用的是void f(int),而f<int>(1)调用的是template<typename T> void f(T);。

     

    你应该把友元函数声明为函数模板

    template<typename T1>

    friend std::ostream& operator<<(std::ostream &, const B<T1> &);



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    • 已标记为答案 lhlzhxh 2010年4月14日 3:02
    2010年4月13日 23:52
    版主
  • 啊,太谢谢诸位的热心回答了,我现在终于明白了。

    其实,我在模板类B中这样声明友元就可以了:friend std::ostream& operator<< <T> (std::ostream &, const B<T> &);

    我的本意是这样的,我要限制std::ostream& operator<< <int>(std::ostream &, const B<int>&)只作为class B<int>的友元,而不能作为class B<float>的友元(不管这样做是不是有必要),也就是说类型要对应(这叫做绑定友元声明),哈哈,我参照《C++ Primer》第三版的说明声明了友元,却出了错误,现在看来那本书是有错误的,它忘记了在operator<<后面加上<T>就像其他绑定的友元函数模板一样。

    版主的回复提醒了我,谢谢了。


    LHL
    • 已标记为答案 lhlzhxh 2010年4月14日 3:21
    2010年4月14日 3:02

全部回复

  • template
    <typename
     T>
    std::ostream& operator <<(std::ostream &o, const B<T> &b)

    只是一个模板函数。

    类型T,与

    template
    <typename
     T>
    class B

    中的类型T没有关系。

    在声明友元时

    friend
     std::ostream& operator
    <<(std::ostream &, const
     B<T> &);

    并不能将这个有元函数理解为函数的一部分。

     


    麻烦把正确答案设为解答。
    • 已标记为答案 lhlzhxh 2010年4月14日 3:06
    2010年4月2日 1:16
    版主
  • 编译是可以通过的,但是链接时提示我没有找到operator<<函数。所以应该不是语法的问题。

    另外,参看《C++ Primer》第三版第684页,“16.4.1 Queue 和QueueItem 的友元声明”,第685页说到了声明友元全局操作符operator<<的方法。

    我的语法和书上应该是一样的,可是编译通过了,却链接不了。


    LHL
    2010年4月2日 8:23
  • 请大家帮我看看吧,这究竟是怎么回事?真的搞不明白。
    LHL
    2010年4月6日 4:58
  • #pragma once
    #include <iostream>
    
    template<typename T>
    class B;
    
    template<typename T>
    std::ostream& operator<<(std::ostream &o, const B<T> &b)
    {
    	o<<b.b<<std::endl;
    	return o;
    }
    
    template<typename T>
    class B
    {
    	friend std::ostream& operator<<(std::ostream &, const B<T> &);
    public:
    	explicit B(int _b = 0) : b(_b)
    	{
    	}
    private:
    	int b;
    };
    

    以上是test.h文件,文件main.cpp是这样的

    #include <iostream>
    #include "test.h"
    using namespace std;
    
    int main()
    {
    	B<int> b(2);
    
    	cout<<b;
    	return 0;
    }
    
    

    请问,为什么链接时会出现错误:

    1>main.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class B<int> const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$B@H@@@Z) referenced in function _main
    1>C:\Users\lhl\Documents\Visual Studio 2008\Projects\lhlproj\Debug\test2.exe : fatal error LNK1120: 1 unresolved externals

    如果友元声明是这样:

    template<typename _T>
    friend std::ostream& operator<<(std::ostream &, const B<_T> &);
    

    就可以链接通过。为什么?注意:编译可以通过,但是链接的时候过不了!

    这个问题一直困扰于我,希望有高人能给予回答,先谢谢了!


    LHL
    2010年4月13日 12:56
  • Hi lhlzhxh.

    如果友元声明是这样:

    template<typename _T>
    friend std::ostream& operator<<(std::ostream &, const B<_T> &);
    
    在我机器上测试OK,能够正常编译及输出。我的编译器是VS2008.
    请:
    1.确定你的编译器是否支持这样的模板语法,能否告知你的编译器类型。
    2.若编译器没有问题,请clean,然后rebuild all 试一试。
    ------
    Bob.Shao
    • 已标记为答案 lhlzhxh 2010年4月14日 3:06
    2010年4月13日 13:59
  • 前面已经说过了,函数模板和类模板特化不是一回事。你的友元函数声明特化后成为

     

    ostream & operator << <int> (std::ostream &, const B<int> )

    而不是你前面声明的

    template<T>

    ostream & operator << (std::ostream &, const B<T> )

    在C++里面通过模板特化的函数和没有通过模板特化的函数是不同的。例如同时声明两个函数

    template<typename T> void f(T);

    void f(int);

    f(1)调用的是void f(int),而f<int>(1)调用的是template<typename T> void f(T);。

     

    你应该把友元函数声明为函数模板

    template<typename T1>

    friend std::ostream& operator<<(std::ostream &, const B<T1> &);



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    • 已标记为答案 lhlzhxh 2010年4月14日 3:02
    2010年4月13日 23:52
    版主
  • 啊,太谢谢诸位的热心回答了,我现在终于明白了。

    其实,我在模板类B中这样声明友元就可以了:friend std::ostream& operator<< <T> (std::ostream &, const B<T> &);

    我的本意是这样的,我要限制std::ostream& operator<< <int>(std::ostream &, const B<int>&)只作为class B<int>的友元,而不能作为class B<float>的友元(不管这样做是不是有必要),也就是说类型要对应(这叫做绑定友元声明),哈哈,我参照《C++ Primer》第三版的说明声明了友元,却出了错误,现在看来那本书是有错误的,它忘记了在operator<<后面加上<T>就像其他绑定的友元函数模板一样。

    版主的回复提醒了我,谢谢了。


    LHL
    • 已标记为答案 lhlzhxh 2010年4月14日 3:21
    2010年4月14日 3:02
  • 这个还需要在前面增加声明

    先声明类,再声明operator<<

    再定义类,再定义operator,才能生效。 如下:

    #include <iostream>

    #include <string>
    #include <cstdlib>

    using namespace::std;
    enum CursorMovements {
    HOME,
    FORWARD,
    BACK,
    UP,
    DOWN,
    END
    };
    template <class Type>
    class QueueItem
    {
    public:
        QueueItem(const Type & t): item(t), next(NULL){};
        Type item;
        QueueItem *next;
        static QueueItem *free_list;
        static const unsigned QueueItem_chunk;
    };
    template <class Type>
    class Queue;
    template <class Type>
    ostream & operator<< (ostream & os, const Queue <Type> &q);

    template <class Type>
    class Queue{
    public:
        //template<typename T1>
        //friend ostream & operator << (ostream & os, const Queue<T1> &);
        friend ostream & operator << <Type> (ostream & , const Queue<Type> &);
        Queue():front(NULL), back(NULL) {};
        ~Queue();
        Type remove();
        void  add(const Type &);
        bool is_empty() const {return NULL== front;};
        //int get_count();
        bool is_full();
    private:
        QueueItem<Type> *front;
        QueueItem<Type> *back;
        int count;
        //
    };
    /*
    template <class Type>
    class Foo
    {
    public:
        void bar(){cout<<"this is Foo:: bar"<<endl;};
    };*/
    template <class Type>
    Queue<Type>:: ~Queue()
    {
        while(!is_empty()){
            remove();
        }
    }
    template <class Type>
    void Queue<Type>:: add(const Type & val)
    {
        QueueItem<Type> *ptr = new QueueItem<Type> (val);
        //QueueItem<Type> *ptr = new QueueItem<Type>;
        if(NULL == ptr){
            cout<<"Queue::add malloc failed"<<endl;
            return;
        }
        ptr->next = NULL;
        if(is_empty()){
            front = back = ptr;
        }
        else{
            back->next = ptr;
            back = ptr;
        }
        count++;
    }
    template <class Type>
    Type Queue<Type>:: remove()
    {
        if(is_empty()){
            cout<<"remove() on empty queue"<<endl;
            exit(-1);
        }
        QueueItem<Type> *ptr = front;
        if(NULL == ptr){
            cout<<"remove() on empty queue"<<endl;
            exit(-1);       
        }
        Type val = ptr->item;
        front = front->next;
        delete ptr;
        return val;
    }
    /*
    int Queue:: get_count()
    {
        return count;
    }*/
    template <class Type>
    ostream & operator<< (ostream & os, const Queue <Type> &q)
    {
       os<<"this is operator<<"<<endl;
       os<<"<";
        QueueItem<Type> *pItem = q.front;
        while(NULL != pItem){
            os<<"item is "<<pItem->item<<"  ";
            pItem = pItem->next;
        }
        os<<">"<<endl;
        return os;
    }
    int main()
    {
       
        Queue<int> memList;
        int iCnt = 0;
        for(iCnt = 0; iCnt < 10; iCnt++){
            memList.add(iCnt);
        }
        cout<<(memList);
        while(!memList.is_empty()){
            iCnt = memList.remove();
            cout<<"remove value is "<<iCnt<<endl;
        }
        //delete memList;
       return 0;
    }

     

    2011年1月7日 8:58