C++ Template Function Compiles in Header But Not Implementation

Why can templates only be implemented in the header file?

Caveat: It is not necessary to put the implementation in the header file, see the alternative solution at the end of this answer.

Anyway, the reason your code is failing is that, when instantiating a template, the compiler creates a new class with the given template argument. For example:

template<typename T>
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f;

When reading this line, the compiler will create a new class (let's call it FooInt), which is equivalent to the following:

struct FooInt
{
int bar;
void doSomething(int param) {/* do stuff using int */}
}

Consequently, the compiler needs to have access to the implementation of the methods, to instantiate them with the template argument (in this case int). If these implementations were not in the header, they wouldn't be accessible, and therefore the compiler wouldn't be able to instantiate the template.

A common solution to this is to write the template declaration in a header file, then implement the class in an implementation file (for example .tpp), and include this implementation file at the end of the header.

Foo.h

template <typename T>
struct Foo
{
void doSomething(T param);
};

#include "Foo.tpp"

Foo.tpp

template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}

This way, implementation is still separated from declaration, but is accessible to the compiler.

Alternative solution

Another solution is to keep the implementation separated, and explicitly instantiate all the template instances you'll need:

Foo.h

// no implementation
template <typename T> struct Foo { ... };

Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

If my explanation isn't clear enough, you can have a look at the C++ Super-FAQ on this subject.

C++ template function compiles in header but not implementation

The problem you're having is that the compiler doesn't know which versions of your template to instantiate. When you move the implementation of your function to x.cpp it is in a different translation unit from main.cpp, and main.cpp can't link to a particular instantiation because it doesn't exist in that context. This is a well-known issue with C++ templates. There are a few solutions:

1) Just put the definitions directly in the .h file, as you were doing before. This has pros & cons, including solving the problem (pro), possibly making the code less readable & on some compilers harder to debug (con) and maybe increasing code bloat (con).

2) Put the implementation in x.cpp, and #include "x.cpp" from within x.h. If this seems funky and wrong, just keep in mind that #include does nothing more than read the specified file and compile it as if that file were part of x.cpp In other words, this does exactly what solution #1 does above, but it keeps them in seperate physical files. When doing this kind of thing, it is critical that you not try to compile the #included file on it's own. For this reason, I usually give these kinds of files an hpp extension to distinguish them from h files and from cpp files.

File: dumper2.h

#include <iostream>
#include <string>
#include <vector>

void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);
#include "dumper2.hpp"

File: dumper2.hpp

template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;

vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;

}

3) Since the problem is that a particular instantiation of dumpVector is not known to the translation unit that is trying to use it, you can force a specific instantiation of it in the same translation unit as where the template is defined. Simply by adding this: template void dumpVector<int>(std::vector<int> v, std::string sep); ... to the file where the template is defined. Doing this, you no longer have to #include the hpp file from within the h file:

File: dumper2.h

#include <iostream>
#include <string>
#include <vector>

void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);

File: dumper2.cpp

template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;

vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;
}

template void dumpVector<int>(std::vector<int> v, std::string sep);

By the way, and as a total aside, your template function is taking a vector by-value. You may not want to do this, and pass it by reference or pointer or, better yet, pass iterators instead to avoid making a temporary & copying the whole vector.

If I've separated a template into a header and source, is there any way to compile it to its own object file?

Is there no way at all around this?

Yes, there is. If you instantiate a template explicitly in the translation unit where the functions are defined, then you can use those instances in other translation units.

But that of course limits what template arguments can be used to those that you've chosen for explicit instantiation. For unconstrained template arguments, there's no way around defining the functions in all translation units (where they are ODR-used).

c++ template trait -- compiling without including header

#include is, for most practical purposes, a request to dump the contents of the include-ed file directly into your source code.

So even though generic_alg.H uses stuff it doesn't define or include, the only thing getting directly compiled is main.C, and the complete definition from trait_implementation.H is dumped into main.C directly before generic_alg.H, so the definition exists when it gets around to compiling the code from generic_alg.H.

Even though it works, it's still a bad idea, because now every source file that uses generic_alg.H must explicitly include trait_implementation.H first, and there is no obvious documentation making that dependency clear.

c++ template and header files

Headers.

It's because templates are instantiated at compile-time, not link-time, and different translation units (roughly equivalent to your .cpp files) only "know about" each other at link-time. Headers tend to be widely "known about" at compile-time because you #include them in any translation unit that needs them.

Read https://isocpp.org/wiki/faq/templates for more.

Definition of a template function inside .cpp file is not working even though I am instantiating a dummy object in .cpp file

Your first question was why writing:

template<> class myclass <int>;

causes an undefined reference error. The answer is that template<> declares an explicit specialization: it's saying that there is a special definition of myclass<int>::doSomething that takes the place of the generic definition earlier in the file. However, the definition of the specialization is not provided. Therefore, no definition of myclass<int>::doSomething is emitted when template.cpp is translated.

Your second question was why replacing the explicit instantiation with:

myclass <int> obj;

in template.cpp did not cause myClass<int>::doSomething to be emitted from that translation unit. The answer is twofold:

  1. When the compiler implicitly instantiates myclass<int> when translating template.cpp, it does not implicitly instantiate myclass<int>::doSomething because, at that point, the function's definition is not yet needed.

  2. Even if the compiler were to implicitly instantiate myclass<int>::doSomething during the translation of template.cpp, that implicit instantiation is NOT guaranteed to make that instantiated definition available to other translation units. (The compiler may, in effect, generate it with internal linkage.)

See C++17 [temp]/7

A function template, member function of a class template, variable template, or static data member of a
class template shall be defined in every translation unit in which it is implicitly instantiated (17.7.1) unless
the corresponding specialization is explicitly instantiated (17.7.2) in some translation unit; no diagnostic is
required.

What this is saying is that if you need to call a function template from translation unit 1, but you only want to define that function template in translation unit 2, then translation unit 2 must explicitly instantiate the function template with the desired arguments. An implicit instantiation doesn't count.

(Note that the explicit instantiation of myclass<int> will also implicitly instantiate the definitions of all of myclass<int>'s member functions.)

Templated Classes using header and source file

What is the proper way to do this with the implementation include at the bottom of the header file?

Put the include guards into your header file, including the implementation #include directive:

#ifndef __FOO_H
#define __FOO_H
// Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};

#include "Foo.tpp"

#endif

You may also add the guards to Foo.tpp, but in the situation you posted it will not make much sense.

Would the include guards on my .cpp file prevent the compiler from creating the templates class/function for other types since it can now only be included once?

Typically you don't need include guards in *.cpp files at all, as you don't include them anywhere. Include guards are only needed in those files which are included into multiple translation units. And of course, those guards will not prevent instantiating the templates for other types, since it is what templates are designed for.

Isn't part of the reason for using header files in the first place is to prevent code from being recopied every time it is included, to keep a short compilation time? So what is the performance effect of templated functions (since they have to be defined in header) versus simply overloading a function/class? When should each be used?

Here you raise a big, platform-dependent and a bit opinion-based topic. Historically, include files were used to prevent code copying, as you said. It was enough to include function declarations (headers, not definitions) into multiple translation units, and then link them with a single copy of the compiled code for included functions.

Templates compile much slower than non-template functions, so implementing template export (separate header/implementation compilation for templates) isn't worth saving compilation time.

For some discussions and good answers on template performance, check these questions:

  • Is Template Metaprogramming faster than the equivalent C code?
  • C++ templates for performance?
  • Do c++ templates make programs slow?

In short, sometimes templates allow you to make some decisions in compile time instead of runtime, making the code faster. Anyway, the only proper way to determine if the code became faster or not, is to run performance tests in real-world environment.

Finally, templates are more about design, not about performance. They allow you to significantly reduce code duplication and conform DRY principle. A banal example of it are functions like std::max. A more interesting example is Boost.Spirit, which uses templates for building parsers entirely in compile time.



Related Topics



Leave a reply



Submit