template class, friend operator << overload

10,543

Solution 1

According to this, you have to make the function known as template in your class definition.

class.h

#include <iostream>
using std::ostream;

template <typename T>
class A {
  public:
    ...

    template <typename J> // <-- CAUTION!
    friend ostream &operator<<(ostream &output, const A<J> &a);
};

class.cpp

#include "class.h"
...
template <typename T>
ostream &operator<<(ostream &output, const A<T> &a) {
  // Your implementation
  return output;
}

...
template ostream &operator<<(ostream &output, const A<int> &a);
template ostream &operator<<(ostream &output, const A<float> &a);

If the line template <typename J> is removed, the compilation error "underfined reference" comes.

Solution 2

See FAQ item 35.12 Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?.

Solution 3

Your posted error code says that it is operator>> that is throwing an unresolved external error, not operator<<. In addition, your code won't compile because there is no convert constructor on myClass taking an int. So you have not posted the correct code.

But this works:

#include <iostream>
using namespace std;

template < class T >
class myClass;

template < class T >
ostream & operator << (ostream &, const myClass<T> &);

template < class T >
class myClass{
    public:
        myClass(int) {}
        friend ostream & operator << <>(ostream &, const myClass<T> &);

    private:
        T m_Data;
};

template < class T >
ostream & operator << (ostream & out, const myClass<T> & refClass){
    out << refClass.m_Data << endl;
    return (out);
}

myClass<int>;
myClass<float>;



int main(int argc, char **argv){
    myClass<int> test(5);
    cout << test;
    return 0;
}
Share:
10,543
Javier
Author by

Javier

Updated on June 04, 2022

Comments

  • Javier
    Javier almost 2 years

    I'm trying to overload the "<<" operator for a template class. I've the definition of the class in a .h file and its implementation in a .cpp file.

    /tmp/ccjJIJhO.o: In function `main':
    main.cpp:(.text+0xad): undefined reference to `std::basic_istream<char, std::char_traits<char> >& operator>><int>(std::basic_istream<char, std::char_traits<char> >&, FeatureVector<int>&)'
    main.cpp:(.text+0xba): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& operator<< <int>(std::basic_ostream<char, std::char_traits<char> >&, FeatureVector<int> const&)'
    collect2: ld returned 1 exit status
    

    The class definition:

    common.h

    #include <iostream>
    using namespace std;
    

    featurevector.h

    #ifndef FEATURE_VECTOR_H
    #define FEATURE_VECTOR_H
    
    #include <common.h>
    
    template < class FEAT_TYPE >
    class FeatureVector;
    
    template < class FEAT_TYPE >
    istream & operator >> (istream &, FeatureVector<FEAT_TYPE> &);
    
    template < class FEAT_TYPE >
    ostream & operator << (ostream &, const FeatureVector<FEAT_TYPE> &);
    
    template < class FEAT_TYPE >
    class FeatureVector{
        public:
            FeatureVector(int = 0);
            ...
            friend istream & operator >> <>(istream &, FeatureVector<FEAT_TYPE> & );
            friend ostream & operator << <>(ostream &, const FeatureVector<FEAT_TYPE> &);
            ...
            ~FeatureVector();
    
        private:
            int m_nDim;
            FEAT_TYPE * m_pFeat;
    };
    #endif
    

    featurevector.cpp

    #include <featurevector.h>
    ...
    template < class FEAT_TYPE >
    istream & operator >> (istream & input, FeatureVector< FEAT_TYPE> & refFeat ){
    
        int d;
    
        for(d=0; d < refFeat.getDim(); d++){
            input >> refFeat.m_pFeat[d];
        }
    
        return (input);
    }
    
    template < class FEAT_TYPE >
    ostream & operator << (ostream & output, const FeatureVector< FEAT_TYPE > & refFeat ){
    
        int d;
    
        for(d=0; d < refFeat.getDim(); d++){
            output << refFeat.m_pFeat[d] << " ";
        }
    
        output << endl;
    
        return (output);
    }
    ...
    #include "featurevector-impl.cpp"
    

    featurevector-impl.cpp

    template class FeatureVector<int>;
    //template istream & operator >> <>(istream &, FeatureVector<int> &);
    //template ostream & operator << <>(ostream &, const FeatureVector<int> &);
    

    mylib.h

    #ifndef MY_LIB_H
    #define MY_LIB_H
    #include <featurevector.h>
    #endif
    

    main.cpp

    #include <mylib.h>
    #include <common.h>
    
    int main(){
        FeatureVector<int> pFeat(10);
        cin >> (pFeat);
        cout << (pFeat);
    
        return (0);
    }
    

    Makefile associated with "mylib"

    INC=./inc
    SRC=./src
    LIB=./lib
    OBJ=./obj
    
    CC=g++
    CFLAGS=-O3 -Wall
    
    mylib: $(LIB)/mylib.a
    echo "mylib was created!..."
    
    $(LIB)/mylib.a: \
    $(OBJ)/featurevector.o 
        ar csr $(LIB)/mylib.a \
    $(OBJ)/featurevector.o 
    
    $(OBJ)/featurevector.o: $(SRC)/featurevector.cpp
        $(CC) -c $(CFLAGS) $(SRC)/featurevector.cpp -I$(INC)  \
        -o $(OBJ)/featurevector.o
    
    clean:
        rm -rf $(LIB)/*.a
        rm -rf $(OBJ)/*.o
    

    Makefile for main.cpp (the main.cpp with its Makefile are under an "app" directory)

    LIB=../lib
    INC=../inc
    OBJ=../obj
    BIN=../bin
    
    CC=g++
    CFLAGS=-O3 -Wall
    LFLAGS=-lmylib -lm
    
    [email protected]: $(LIB)/mylib.a [email protected]
        cd ..; make; cd app;
    $(CC) $(CFLAGS) [email protected] -o $(BIN)/$@ -I$(INC) -L$(LIB) $(LFLAGS)
    
    clean:
        rm -rf $(BIN)/*
    
  • John Dibling
    John Dibling over 13 years
    I don't think this is the issue. Javier is #include-ing the HPP file directly from the H file, yielding the same results as your FAQ link.
  • Javier
    Javier over 13 years
    Exactly, I considered the suggestion in the referred link already. Thanks anyway.
  • Javier
    Javier over 13 years
    Hi, as I mentioned, I didn't put the whole class definition. I have of course a default constructor. So, your suggestion is to put everything in the same file? This was the problem? Due to organization purposes, I prefer to keep the files separated.
  • John Dibling
    John Dibling over 13 years
    @Javier: No, ultimately the problem I was having is that your posted error message complained about operator>>, not operator<<
  • John Dibling
    John Dibling over 13 years
    @Javier: Also, it would be very helpful if you could post a minimal and complete example that replicates the problem you are having. Psudocode like you've posted is difficult to work with.
  • Cheers and hth. - Alf
    Cheers and hth. - Alf over 13 years
    @John, @Jahvier: I can't see that the definition of the operator>> function template is available in main. Nor can I see an explicit instantiation of it. If it is, sorry for the noise. But I gather the question wouldn't have been raised if it is. CHeers & hth.,
  • Javier
    Javier over 13 years
    Hi John, well, I updated already the class, because I was trying to overload both ">>" and "<<" and the similar error was shown.
  • Javier
    Javier over 13 years
    I edited the class, so the definition of the operator >> is already included.
  • Cheers and hth. - Alf
    Cheers and hth. - Alf over 13 years
    @Javier: well, how about telling us whether that got rid of the problem, and if not, posting sort of actual code?
  • Javier
    Javier over 13 years
    Hi Steve, I recently tried your suggestion, but I still have the same error. I updated my initial post, were I included the true class.
  • Javier
    Javier over 13 years
    It didn't solve and I also included on my original post the actual code.
  • Steve M
    Steve M over 13 years
    What is your compilation command? Are you compiling both main.cpp and featurevector.cpp? (Or main.o and featurevector.o if you compiled them separately)
  • Cheers and hth. - Alf
    Cheers and hth. - Alf over 13 years
    @Javier the friend functions, like operator>>, aren't class members. They don't get instantiated by instantiating the class. Cheers & hth.
  • Javier
    Javier over 13 years
    @Steve, I'm compiling them separately.
  • Steve M
    Steve M over 13 years
    @Javier: So when you link them, are you specifying both object files in the command? This compiles, links, and runs for me (even with separate compilation) if I put explicit instantiations for the stream operators in featurevector-impl.cpp.
  • Cheers and hth. - Alf
    Cheers and hth. - Alf over 13 years
    @Javier: at the time of my comment, and still as I'm writing this, you're instantiating your class for type int. But you're not instantiating the operator templates (which are not part of the class). Since definitions not available in main, and since no instantiations, linker error. See FAQ item 35.12 linked to above. Cheers & hth.
  • Steve M
    Steve M over 13 years
    @Alf: This is covered by my answer, but he still seems to be having some kind of problem.
  • Javier
    Javier over 13 years
    Hi Steve, I basically have a small library that contains the featurevector class (so, I generate at this point featurevector.o). Then, when I compile the main, I link the library, sth, like: g++ -O3 -Wall main.cpp -o ../bin/main -I../inc -L../lib -lmylib -lm
  • Javier
    Javier over 13 years
    @Steve, it seems to be that the problem is during the linking. Could you please check the Makefile I added in my original post? Do you see sth. strange there?
  • Javier
    Javier over 13 years
    It seems to be that the problem is during the linking (from what I found in the internet, g++ & gcc). Could you please check the Makefile I added in my original post? Do you see sth. strange there?