3

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

$@.cpp: $(LIB)/mylib.a $@.cpp
    cd ..; make; cd app;
$(CC) $(CFLAGS) $@.cpp -o $(BIN)/$@ -I$(INC) -L$(LIB) $(LFLAGS)

clean:
    rm -rf $(BIN)/*
Javier
  • 1,131
  • 4
  • 17
  • 22
  • friend ostream & operator << <> is this a typo ? – bjskishore123 Nov 10 '10 at 17:54
  • 2
    Is it the same code that created the error? The error is on operator >> not <<. – Rod Nov 10 '10 at 17:56
  • Works fine: http://www.ideone.com/H9xjs – kennytm Nov 10 '10 at 17:59
  • Hi guys, I edited the original post and included the actual code. – Javier Nov 10 '10 at 19:14
  • 1
    Never #include a .cpp file from a header. Simply name it differently: blah_detail.hpp, _blah.inc, etc. –  Nov 10 '10 at 19:16
  • @Roger: I've heard of `.ipp` or `.tpp`; I'd probably go with the latter if I ever adopted this style. – GManNickG Nov 10 '10 at 19:24
  • @Roger, @GMan: He's not including it into a header, he's including it into another source file. It seems that he wants to compile the instantiations of `>>` separately. (I'm not condoning this or the way he's going about it.) – Steve M Nov 10 '10 at 19:46
  • @Steve: Ah, you're right. It should still be *very rarely* done in .cpp files. –  Nov 10 '10 at 19:50
  • Hi guys, I was surfing in the internet and it seems to be a linking problem (g++ and gcc). I added the makefile that i'm using. Could you please check if it's OK? Because I couldn't solve the problem yet. – Javier Nov 11 '10 at 10:23
  • @Javier: in the code as it is when I'm writing this, with a makefile added, [featurevector-impl.cpp] still only instantiates the class template, not the operator templates. The operator template instantiations are still commented out. Cheers & hth., – Cheers and hth. - Alf Nov 11 '10 at 13:41

5 Answers5

4

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.

Community
  • 1
  • 1
chehsunliu
  • 1,559
  • 1
  • 12
  • 22
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?.

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 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. – John Dibling Nov 10 '10 at 18:15
  • Exactly, I considered the suggestion in the referred link already. Thanks anyway. – Javier Nov 10 '10 at 18:29
  • @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., – Cheers and hth. - Alf Nov 10 '10 at 18:39
  • I edited the class, so the definition of the operator >> is already included. – Javier Nov 10 '10 at 18:45
  • @Javier: well, how about telling us whether that got rid of the problem, and if not, posting sort of *actual* code? – Cheers and hth. - Alf Nov 10 '10 at 18:51
  • It didn't solve and I also included on my original post the actual code. – Javier Nov 10 '10 at 19:12
  • @Javier the friend functions, like operator>>, aren't class members. They don't get instantiated by instantiating the class. Cheers & hth. – Cheers and hth. - Alf Nov 10 '10 at 19:38
  • @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. – Cheers and hth. - Alf Nov 10 '10 at 20:00
  • @Alf: This is covered by my answer, but he still seems to be having some kind of problem. – Steve M Nov 10 '10 at 20:04
  • 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? – Javier Nov 11 '10 at 10:34
1

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;
}
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • 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. – Javier Nov 10 '10 at 18:31
  • @Javier: No, ultimately the problem *I* was having is that your posted error message complained about `operator>>`, not `operator<<` – John Dibling Nov 10 '10 at 18:36
  • @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. – John Dibling Nov 10 '10 at 18:37
  • Hi John, well, I updated already the class, because I was trying to overload both ">>" and "<<" and the similar error was shown. – Javier Nov 10 '10 at 18:41
0

featurevector-impl.cpp is incorrect. Explicit template instantiations look like this:

template class FeatureVector<int>;

Since the operators aren't members, they must also be explicitly instantiated:

template istream & operator >> <>(istream &, FeatureVector<int> &);

I don't recommend splitting up your template definitions like this, though, unless you're really keen on micromanaging which specific classes will work with your template (which kind of goes against the spirit of using a template).

Steve M
  • 8,246
  • 2
  • 25
  • 26
  • 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 Nov 10 '10 at 19:12
  • 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) – Steve M Nov 10 '10 at 19:36
  • @Steve, I'm compiling them separately. – Javier Nov 10 '10 at 19:43
  • @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. – Steve M Nov 10 '10 at 19:48
  • 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 Nov 10 '10 at 20:35
  • @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 Nov 11 '10 at 10:29
0

Don't make it so complicated:

template<class FEAT_TYPE>
struct FeatureVector {
  FeatureVector(int = 0);

  friend std::istream& operator>>(std::istream &s, FeatureVector &x) {
    for(int d = 0; d < x.getDim(); d++) {
      s >> x.m_pFeat[d];
    }
    return s;
  }

  friend std::ostream& operator<<(std::ostream &s, FeatureVector const &x) {
    // since you're terminating with " " rather than separating:
    copy(x.m_pFeat, x.m_pFeat + x.getDim(), ostream_iterator<FEAT_TYPE>(s, " "));
    s << endl;
    return s;
  }

  //...
  • Thanks for the suggestions! but I need to work with classes for some reasons. – Javier Nov 10 '10 at 19:27
  • @Javier: 'struct' declares a class. –  Nov 10 '10 at 19:33
  • @Roger: thanks, I didn't know that. Where was the problem in my methods, I didn't understand what was wrong. – Javier Nov 10 '10 at 19:36
  • @Javier: I'm not sure exactly what the problem is as the question has been updated with more information and I'm reluctant to assume to know what you're using, but as the question text stands right now, the definitions of those functions are commented out. — My answer here is saying you're making this *much* too complicated, and showing how to avoid that. –  Nov 10 '10 at 19:47