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 #include
d 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:
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 notPropertyType because otherwise the new
PropertyTypewill shadow the outer
PropertyType`.Added a low-level
const
to the second parameter of the overloadedopeartor<<
.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:
- Header files which declare function templates or member function templates.
- Template definition files which are pretty much like header files but define the [member] function templates declared in their respective header.
- 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
Is It True That There Is No Need to Learn C Because C++ Contains Everything
Linear Index Upper Triangular Matrix
Qt: How to Handle the Event of the User Pressing the 'X' (Close) Button
How Much Overhead Is There in Calling a Function in C++
C++ Most Efficient Way to Convert String to Int (Faster Than Atoi)
Do Polymorphism or Conditionals Promote Better Design
Large 2D Array Gives Segmentation Fault
Templates: Template Function Not Playing Well with Class's Template Member Function
What Is a Good Easy to Use Profiler for C++ on Linux
How to Receive a Lambda as Parameter by Reference
Question About a Function Definition (Three Dots in Parameters..)
How to Initialize All Elements in an Array to the Same Number in C++