Why should template class functions be declared in the same translation unit?

Take this code, for example:

/* * foo.h * * Created on: Nov 5, 2011 * Author: AutoBotAM */ #ifndef FOO_H_ #define FOO_H_ template<typename Type> class Foo { public: void Bar(Type object); }; #endif /* FOO_H_ */ 

.

 /* * foo.cpp * * Created on: Nov 5, 2011 * Author: AutoBotAM */ #include <iostream> using namespace std; #include "foo.h" template<typename Type> void Foo<Type>::Bar(Type object) { cout << object; } 

.

 /* * main.cpp * * Created on: Oct 15, 2011 * Author: AutoBotAM */ #include <iostream> using namespace std; #include "foo.h" Foo<int> foo; int main() { cout << "The year is "; foo.Bar(2011); return 0; } 

This is how I usually talk about declaring classes without templates. Unfortunately, this code leads to the error ../src/main.cpp:18: undefined reference to 'Foo<int>::Bar(int)' (in MinGW). I did some reading, and it turns out you need to declare the template classes in the same translation unit, for example:

 /* * foo.h * * Created on: Nov 5, 2011 * Author: AutoBotAM */ #ifndef FOO_H_ #define FOO_H_ template<typename Type> class Foo { public: void Bar(Type object) { cout << object; } }; #endif /* FOO_H_ */ 

My big question is: why do you need this? I could imagine several pitfalls in development with this scheme. For example, suppose we had 50 translation units #including foo.h , and we make changes to void Foo::Bar(Type) . Since Bar is in the header file, we have to wait until all 50 translation units have been compiled before we get any results. If we had Bar working separately in foo.cpp , we would have to wait until 1 translation unit was compiled. Are there any ways to overcome this problem?

Thanks for any advice!

+4
source share
4 answers

Templates are not types. These are just templates. They become only a type when they are created (with a full set of parameters).

Compilation requires that all necessary type definitions be available at compile time. Moreover, binding requires that all necessary function definitions, including member functions, exist in some translation unit (with an attachment providing the usual exception for a rule with one definition).

If you put all this together, it almost automatically follows that the entire definition of the function of the template member of the template class should be available for each instance of the template used at some point in the compilation process.

On the other hand, consider the following setting:

 // Header file: template <typename T> struct Foo { void f(); } // "Implementation" file template <typename T> void Foo::f() { /* stuff */ } 

If you compile an implementation file, it does not contain any code, since the template instance is not compiled. The user of the header file can create an instance of Foo<int> , but the class body is never created in any TU, so you get a linker error when building the program.

This can help think of patterns as a tool for generating code, rather than actual code, at least for compilation.

+2
source

Templates lie somewhere between compilation and link time. The pending implementation can do a lot after viewing the template declaration, but the actual code cannot be generated until the template is created with its template arguments.

You may have template class functions in the cpp file, and you can explicitly create it using the arguments you will use. However, you can only use these instances in your program. For example, you can add foo.cpp

 template class Foo<int>; 

Now you can use Foo<int> anywhere, even if the implementations are in the translation's own part. However, you cannot use any other type of Foo<> , since the linker cannot find its functions (they actually do not exist).

+3
source

Metaprogramming patterns. They do not compile (directly) into object code. Only the results of them.

0
source

Most compilers do not yet support external templates, which will allow you to use the cpp / h type you are looking for. However, you can still separate template declarations from implementations that are similar to what you want. Place the declarations in the .h files, place the implementations in a separate source file with any extension you want (.i and .ipp are popular), and then #include source file at the bottom of the .h file. The compiler sees a single translation unit, and you get code splitting.

 /* * foo.h * * Created on: Nov 5, 2011 * Author: AutoBotAM */ #ifndef FOO_H_ #define FOO_H_ template<typename Type> class Foo { public: void Bar(Type object); }; #include "foo.ipp" #endif /* FOO_H_ */ 

.

 /* * foo.ipp * * Created on: Nov 5, 2011 * Author: AutoBotAM */ #include <iostream> template<typename Type> void Foo<Type>::Bar(Type object) { std::cout << object; } 
0
source

Source: https://habr.com/ru/post/1379825/


All Articles