4

I have this function declaration:

template<class T>
a::A& a::A::operator<<(T out) {
    std::cout << out;
    return (*this);
}

and this function definition:

namespace a {
    ...
    class A {
        ...
        template<class T> A& operator<<(T);

And I call it as:

a::A b;
b << 1;

and this is the Makefile:

app: main.o A.o
    g++ main.o A.o -o app

main.o: main.cpp
    g++ -c main.cpp

A.o: A.cpp
    g++ -c A.cpp

and it gives me:

Undefined symbols: a::A& a::A::operator<< <int>(int)

why is that?

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • 2
    Is the call in the same file as the function definition? Or are they divided, e.g. between .h and .cpp? – jogojapan Oct 31 '12 at 02:28
  • @jogojapan, they are divided. Every pieace of each code is in a separated file. The first into `A.cpp`, the second into `A.hpp` and the third in `main.cpp`. – Shoe Oct 31 '12 at 02:33
  • 3
    That's the problem then. You must either include the definition (btw you confuse declaration and definition in the text of your question) in the .hpp file, or, alternatively, put an explicit instantiation for `int` in the .cpp file. See here as well: http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file – jogojapan Oct 31 '12 at 02:37

2 Answers2

6

The function template will be turned into an actual function at compile time, once the type represented by T (that is, int in your case) is actually known. However, this is not the case before main.cpp is compiled. At the time when A.cpp is compiled, the template function is not instantiated into an actual function, therefore the object file generated doesn't include the binary version of the function.

There are two ways to solve this.

  1. Include the function definition in your header file. That is, make

    template<class T>
    a::A& a::A::operator<<(T out) {
        std::cout << out;
        return (*this);
    }
    

    a part of the header file, and remove the function definition from the .cpp file.

    The effect of this is that any .cpp file that includes this header will be able to use any instantiation of the template, i.e. for any value of T.

  2. Alternatively, include an explicit template instantiation statement in A.cpp:

    template a::A& a::A::operator<<(int out);
    

    This will cause the compiler to actually instantiate the template when A.cpp is compiled, and to include the compiled function in the object file. Hence the linker can find it when linking main.o and A.o together, and all is fine. The disadvantage is that it will only work for the specific types (in this case, only int) that you provided explicit instantiations for.

jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • 1
    It works for me... both declaration and definition are in .hpp now, correct? And there is nothing related to this function left in the .cpp file, right? Have you deleted the old .o files and recompiled everything? – jogojapan Oct 31 '12 at 03:00
-1

Try changing your definition to:

template<class T>
a::A& a::A::operator<< <T> (T out) {
    std::cout << out;
    return (*this);
}

?

(Make sure it's in the header file)

Serge
  • 1,974
  • 6
  • 21
  • 33
  • This is incorrect syntax. The primary template definition doesn't allow for the type to be specified in angle brackets like this. (And if this was a template specialization, the type enclosed in angle brackets wouldn't be a template parameter.) – jogojapan Oct 31 '12 at 02:52
  • 1
    @jogojapan Well, alright, my mistake. I typically template classes, not functions or members. Sorry. – Serge Oct 31 '12 at 03:05