views:

53

answers:

3

These are my files:

--------[ c.hpp ]--------

#ifndef _C
#define _C

#include<iostream>
class C
{
public:
    template<class CARTYPE> void call(CARTYPE& c);
};

#endif

--------[ c.cpp ]--------

#include "c.hpp"

template<class CARTYPE> void C::call(CARTYPE& c)
{
    //make use of c somewhere here
    std::cout<<"Car"<<std::endl;
}

--------[ v.cpp ]--------

class Merc
{};

--------[ main.cpp ]--------

#include "c.hpp"
#include "v.cpp"
//#include "c.cpp"

int main()
{
    Merc m; 
    C someCar;
    someCar.call(m);

}//main

I'm able to generate ".o" files for all the above files, with the command g++ -c main.cpp and g++ -c c.cpp and so on. But when I try linking the ".o" files with g++ -o car c.o main.o v.o I get this error:

main.o: In function `main':
main.cpp:(.text+0x17): undefined reference to `void C::call<Merc>(Merc&)'
collect2: ld returned 1 exit status

The error goes away when I uncomment the line #include "c.cpp" in main.cpp, but I feel it may be bad practice to include the cpp file this way. Am I doing this wrong? Is there a better way to cater to templated declarations while creating separate object files and linking them? p.s: I'm using the templated function in a much more complex class structure. What is shown here is just a small example for the sake of showing you the kind of error I'm facing.

+2  A: 

I believe this is because when you compile c.cpp the compiler doesn't know it needs to generate code for C::call<Merc>(Merc&), and when you're compiling main.cpp it doesn't have the definition of C::call<T>(T&) from which to instantiate C::call<Merc>(Merc&).

Template definitions are essentially semantic macros, so much like the preprocessor's #define lexical macros they must be visible in the compilation units that use them.

Jeffrey Hantin
+1  A: 

A way to solve this problem is to

a. remove '#include "c.hpp"' from c.cpp AND

b. include 'c.cpp' at the end of 'c.hpp' (strange sounding '#include "c.pp"')

This way the template definitions are availabe to each translation unit that includes 'c.hpp' without explicitly doing so in each .cpp file. This is called the 'inclusion model'

Chubsdad
It also causes trouble if you evolve a need for things in c.cpp that shouldn't be included in each translation unit.
Tony
@Tony: Oh, so what do you do then?
Chubsdad
Then you really do have to move the template implementation into the header. Just thinking of the One Definition Rule and all that jazz....
Tony
@Tony:Ok, and then explicitly specialize the templates in the header file right?
Chubsdad
Yes, all template implementation must be visible at the points of use, so if the specialisations are used by other cpps, then the header is the best place to share them (assuming we've crossed the point of abandoning the inclusion model you started with). Even for private templates, it would be pretty confusing not to localise specialisations with general implementations so I'd say they all out to be together, whether in the .h or .cpp.
Tony
A: 

Essentially, you cannot have related template declarations and definitions in separate files in current C++. You should fully define class C in one file.

nakiya
Just by way of clarification: that's true for the common and illustrated case of public/protected templates, but not necessary for private templates.
Tony
what happens with friend classes and private templates?
nakiya