C++ Templates Declare in .H, Define in .Hpp

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++ templates declare in .h, define in .hpp

Typically (in my experience, YMMV) an hpp file is an #include-ed CPP file. This is done in order to break the code up in to two physical files, a primary include and an implementation-details file that the users of your library don't need to know about. It is done like this:

super_lib.h (the only file your clients need to #include)

template<...> class MyGizmo
{
public:
void my_fancy_function();
};

#include "super_lib_implementation.hpp"

super_lib_implementation.hpp (your clients do not #include this directly)

template<...> void MyGizmo<...>::my_fancy_function()
{
// magic happens
}

Templates, coding only in header file?

Separating the declaration and the interface documentation from the implementation like you generally do with header (*.h) and source files (*.cpp) is generally not possible with templates due to the C++ compilation process.

In the compilation process all the header files are just included into the source files they are called from, then these source files are processed independently and finally all the resulting object files linked together into an executable. Templates are functions for generic data-types which are only instantiated for the data-types they are called with. This means a template function does not create any code as long as it is not instantiated by somebody with valid template arguments. Somehow you have to make sure that the template is instantiated with the data types required from all the source files that access the template function.

  • If you put the declaration in the header file and the implementation into the source file (like you generally do for functions) only those versions are instantiated that are know inside this particular source file. This means any other source file requiring a different set of parameters might result in a linker error as it is not aware that the source file does not hold the correct combination of template parameters making this not portable.
  • You could instantiate all the required versions manually which reduces overhead but generally this makes you lose a lot of flexibility as you have to define beforehand which template parameters will be used in other translatory units.
  • Finally you can simple leave the declaration and the implementation in the header file. This slows down the compilation process as all the contents of the header have to be pasted into all the source files calling it. Therefore some people are no fans of heavily templated libraries such as Boost but it gives you the most flexibility. This effect can though be reduced by features like pre-compiled headers. In order to make this more easily readable again some libraries separate declaration (and documentation) from the implementation by putting the declaration in *.h files and define the implementation in *.hpp-files (similar to * .cpp, see here).

So generally it depends on the usage: If you know that a particular template function is only used inside a particular source file you can put documentation, declaration and implementation into the source file (instead of the header). If you want to share the template function with several source files put the declaration and implementation inside the header. This is generally the standard way as it is most flexible. If you write a fully templated library then probably separating declaration and documentation in *.h files and implementation in *.hpp files probably makes the most sense but mainly just for having a simpler overview. Anyways it is completely normal to not have a clean separation between declaration and implementation for some functions and therefore some header files might be lacking a corresponding source file.

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.

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.

Storing C++ template function definitions in a .CPP file

The problem you describe can be solved by defining the template in the header, or via the approach you describe above.

I recommend reading the following points from the C++ FAQ Lite:

  • Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?
  • How can I avoid linker errors with my template functions?
  • How does the C++ keyword export help with template linker errors?

They go into a lot of detail about these (and other) template issues.

How do we declare a friend function with a class template into .h file and define them into a .cpp file (not all in one header file)?

There are two problems with your code snippet.

The first problem is that you've put the implementation in the source file instead of the header file. So to solve this just move the implementation into the header file.

The second problem is that even if you move the implementation into the source file the program will still not work(Demo). This is because the friend declaration that you currently have(for overloaded opearator<<), is for an ordinary(non-template) function. That is, in your original code operator<< for class template Property<> is not function template, but “ordinary” function instantiated with the class template if needed. It is what we call a templated entity.

But the definition that you've provided in the source file(.cpp) for the overloaded operator<< is for a function template and not for an oridnary function. Thus, for the statement std::cout << num << "\n"; the linker cannot find the definition/implementation corresponding to the ordinary overloaded operator<< for which you had the friend declaration.

There are two ways to solve this:

Method 1

Add a separate parameter clause in the friend declaration.

template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
template<typename T> //parameter cluase added here
//---------------------------------------------------vvvvv----------------------->const added here
friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
};
template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}

Demo

Method 2

Here we provide forward declaration for the overloaded operator<<.

//forward declaration for class template Property
template<typename T> class Property;

//forward declaration for overloaded operator<<
template<typename T> std::ostream& operator<<(std::ostream&,const Property<T>&);//note the const in the second parameter
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
//---------------------------------vvvvvvvvvvvvvv---------------------------------> angle brackets used here
friend std::ostream& operator<<<PropertyType>(std::ostream& os,const Property<PropertyType>& other);//also note the const in the second parameter
};
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
m_property = value;
return m_property;
}

template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}

Demo

Method 3

If you want to provide the implementation inside the source file instead of the header file, then you should add

template std::ostream& operator<<(std::ostream& os, Property<int>& other);

inside the source file in addition to adding a template parameter clause for the friend declaration as shown below:

class.h

#ifndef MYCLASS_H
#define MYCLASS_H
#include <iostream>
template <class PropertyType>
class Property {
PropertyType m_property;
public:
const PropertyType& operator=(const PropertyType& value);
template<typename T> //parameter clause added
//---------------------------------------------------vvvvv--------------------->const added here
friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
};

#endif

class.cpp

#include "class.h"

template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
m_property = value;
return m_property;
}

template<typename PropertyType>
//----------------------------------------vvvvv------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
os << other.m_property;
return os;
}
template class Property<int>;
template std::ostream& operator<<(std::ostream& os,const Property<int>& other);

main.cpp


#include <iostream>

#include "class.h"
int main() {
Property<int> num;
num = 100;
std::cout << num << "\n";
}

Demo

The changes that I made include:

  1. Added a separate template parameter clause for the friend declaration. This is so that the friend declaration is for a function template. Moreover, we specify a different type parameter named T and not PropertyType because otherwise the new PropertyTypewill shadow the outerPropertyType`.

  2. Added a low-level const to the second parameter of the overloaded opeartor<<.

  3. In method3, inside source file(class.cpp), added

template std::ostream& operator<<(std::ostream& os,const Property<int>& other);

for the non-member function overloaded operator<<.

*.h or *.hpp for your class definitions

Here are a couple of reasons for having different naming of C vs C++ headers:

  • Automatic code formatting, you might have different guidelines for formatting C and C++ code. If the headers are separated by extension you can set your editor to apply the appropriate formatting automatically
  • Naming, I've been on projects where there were libraries written in C and then wrappers had been implemented in C++. Since the headers usually had similar names, i.e. Feature.h vs Feature.hpp, they were easy to tell apart.
  • Inclusion, maybe your project has more appropriate versions available written in C++ but you are using the C version (see above point). If headers are named after the language they are implemented in you can easily spot all the C-headers and check for C++ versions.

Remember, C is not C++ and it can be very dangerous to mix and match unless you know what you are doing. Naming your sources appropriately helps you tell the languages apart.

Template instantiation in header file

You can put template definitions into implementation (i.e., non-header) files. However, the compiler probably won't do the instantiation for you. The compiler will only implicitly instantiate what is used when the definition is seen. Instead, you would need to do the instantiation yourself. I found it useful in the past to have three kinds of files:

  1. Header files which declare function templates or member function templates.
  2. Template definition files which are pretty much like header files but define the [member] function templates declared in their respective header.
  3. Source files which would include the template definition files as necessary and explicitly instantiate the [member] function templates.

Whether explicit instantiation is feasible for a specific template depends on how many different instantiations are actually being used. It is probably not feasible for algorithms or class templates like std::vector<T> but it is certainly feasible, e.g., for the IOStream templates which have just two instantiations.

Here are a few more details on to organize sources involving templates.



Related Topics



Leave a reply



Submit