views:

1204

answers:

11

g++ -c -o main.o main.cpp
g++ -c -o stack.o stack.cpp
g++ -o main main.o stack.o
main.o: In function `main':
main.cpp:(.text+0xe): undefined reference to 'stack::stack()'
main.cpp:(.text+0x1c): undefined reference to 'stack::~stack()'
collect2: ld returned 1 exit status
make: *** [program] Error 1

stack.hpp:

#ifndef _STACK_HPP
#define _STACK_HPP

template <typename Type>
class stack {
    public:
            stack();
            ~stack();
};
#endif

stack.cpp:

#include <iostream>
#include "stack.hpp"

template <typename Type> stack<Type>::stack() {
        std::cerr << "Hello, stack " << this << "!" << std::endl;
}

template <typename Type> stack<Type>::~stack() {
        std::cerr << "Goodbye, stack " << this << "." << std::endl;
}

main.cpp:

#include "stack.hpp"

int main() {
    stack<int> s;

    return 0;
}

ld is of course correct: the symbols aren't in stack.o.

The answer to question 687448 does not help, as I'm already doing as it says.
This one might help, but I don't want to move every single method into the .hpp file - I should'nt have to, should I?

Is the only reasonable solution to move everything in the .cpp file to the .hpp file, and simply include everything, rather than link in as a standalone object file? That seems awfully ugly! In that case, I might as well revert to my previous state and rename stack.cpp to stack.hpp and be done with it.

+4  A: 

No, it's not possible. Not without the export keyword, which for all intents and purposes doesn't really exist.

The best you can do is put your function implementations in a ".tcc" or ".tpp" file, and #include the .tcc file at the end of your .hpp file. However this is merely cosmetic; it's still the same as implementing everything in header files. This is simply the price you pay for using templates.

Charles Salvia
Your answer is not correct. You can generate code from a template class in a cpp file, given you know what template arguments to use. See my answer for more information.
Benoît
This is just not true.
Martin York
True, but this comes with the serious restriction of needing to update the .cpp file and recompile every time a new type is introduced which uses the template, which is probably not what the OP had in mind.
Charles Salvia
A: 

Because templates are compiled when required, this forces a restriction for multi-file projects: the implementation (definition) of a template class or function must be in the same file as its declaration. That means that we cannot separate the interface in a separate header file, and that we must include both interface and implementation in any file that uses the templates.

ChadNC
A: 

You need to have everything in the hpp file. The problem is that the classes aren't actually created until the compiler sees that they're needed by some OTHER cpp file - so it has to have all the code available to compile the templated class at that time.

One thing that I tend to do is to try to split my templates into a generic non-templated part (which can be split between cpp/hpp) and the type-specific template part which inherits the non-templated class.

Aaron
+2  A: 

Only if you #include "stack.cpp at the end of stack.hpp. I'd only recommend this approach if the implementation is relatively large, and if you rename the .cpp file to another extension, as to differentiate it from regular code.

Evän Vrooksövich
If you're doing this, you'll want to add #ifndef STACK_CPP (and friends) to your stack.cpp file.
Stephen Newell
Beat me to this suggestion. I too don't prefer this approach for style reasons.
luke
A: 

Another possibility is to do something like:

#ifndef _STACK_HPP
#define _STACK_HPP

template <typename Type>
class stack {
    public:
            stack();
            ~stack();
};

#include "stack.cpp"  // Note the include.  The inclusion
                      // of stack.h in stack.cpp must be 
                      // removed to avoid a circular include.

#endif

I dislike this suggestion as a matter of style, but it may suit you.

luke
A: 

The 'export' keyword is the way to separate out template implementation from template declaration. This was introduced in C++ standard without an existing implementation. In due course only a couple of compilers actually implemented it. Read in depth information at Inform IT article on export

Shailesh Kumar
+1  A: 

Sometimes it is possible to have most of implementation hidden in cpp file, if you can extract common functionality foo all template parameters into non-template class (possibly type-unsafe). Then header will contain redirection calls to that class. Similar approach is used, when fighting with "template bloat" problem.

Konstantin
+1 - even though it doesn't work out very well most of the time (at least, not as often as I wish)
peterchen
A: 

The problem is that a template doesn't generate an actual class, it's just a template telling the compiler how to generate a class. You need to generate a concrete class.

The easy and natural way is to put the methods in the header file. But there is another way.

In your .cpp file, if you have a reference to every template instantiation and method you require, the compiler will generate them there for use throughout your project.

new stack.cpp:

#include <iostream>
#include "stack.hpp"
template <typename Type> stack<Type>::stack() {
        std::cerr << "Hello, stack " << this << "!" << std::endl;
}
template <typename Type> stack<Type>::~stack() {
        std::cerr << "Goodbye, stack " << this << "." << std::endl;
}
static void DummyFunc() {
    static stack<int> stack_int;  // generates the constructor and destructor code
    // ... any other method invocations need to go here to produce the method code
}
Mark Ransom
You dont need the dummey function: Use 'template stack<int>;' This forces an instanciation of the template into the current compilation unit. Very useful if you define a template but only want a couple of specific implementations in a shared lib.
Martin York
@Martin: including all of the member functions? That's fantastic. You should add this suggestion to the "hidden C++ features" thread.
Mark Ransom
+2  A: 

If you know what types your stack will be used with, you can instantiate them expicitly in the cpp file, and keep all relevant code there.

It is also possible to export these across DLLs (!) but it's pretty tricky to get the syntax right (MS-specific combinations of __declspec(dllexport) and the export keyword).

We've used that in a math/geom lib that templated double/float, but had quite a lot of code. (I googled around for it at the time, don't have that code today though.)

Marcus Lindblom
+7  A: 

It is possible, as long as you know what instantiations you are going to need.

Add the following code at the end of stack.cpp and it'll work :

template stack<int>;

All non-template methods of stack will be instantiated, and linking step will work fine.

Benoît
In practice, most people use a separate cpp file for this - something like stackinstantiations.cpp.
Nemanja Trifunovic
Agreed. That's what i do as well.
Benoît
+2  A: 

It is not possible to write the implementation of a template class in a seperate cpp file and compile. All the ways to do so, if anyone claims, are workarounds to mimic the usage of seperate cpp file but practically if you intend to write a template class library and distribute it with header and lib files to hide the implementation, it is simply not possible.

To know why, let us look at the compilation process. The header files are never compiled. They are only preprocessed. The preprocessed code is then clubbed with the cpp file which is actually compiled. Now if the compiler has to generate the appropriate memory layout for the object it needs to know the data type of the template class.

Actually it must be understood that template class is not a class at all but a template for a class the declaration and definition of which is generated by the compiler at compile time after getting the information of the data type from the argument. As long as the memory layout cannot be created, the insrtuctions for the method definition cannot be generated. Remember the first argument of the class method is the 'this' operator. All class methods are converted into individual methods with name mangling and the first parameter as the object which it operates on. The 'this' argument is which actually tells about size of the object which incase of template class is unavailable for the compiler unless the user instantiates the object with a valid type argument. In this case if you put the method definitions in a seperate cpp file and try to compile it the object file itself will not be generated with the class information. The compilation will not fail, it would generate the object file but it won't generate any code for the template class in the object file. This is the reason why the linker is unable to find the symbols in the object files and the build fails.

Now what is the alternative to hide important implementation details? As we all know the main objective beind seperating interface from implementation is hiding implementation details in binary form. This is where you must seperate the datastructures and algorithms. Your template classes must represent only datastructures not the algorithms. This enables you to hide more valuable implementation details in seperate non-templatized class libraries, the classes inside which would work on the template classes or just use them to hold data. The template class would actually contain less code to assign, get and set data. Rest of the work would be done by the algorithm classes.

I hope this discussion would be helpful.

Sharjith N.
+1 This is a perfectly good explanation.
Venemo