Explicit instantiation example for a templated function

Jan 21, 2021 at 6:35pm
Hi everyone,

when dealing with templated classes/functions, it's common practice to put both declarations and definitions inside the header, rather than writing them separately and using explicit instatiation.

Let's say we have an [i]header file foo.hpp[\i] where we write the declaration of the templated function foo

1
2
3
4
5
6
7
8
9
#ifndef foo_hpp
#define foo_hpp


template <typename T>
void foo();


#endif /* foo_hpp */ 




Then, we write a [i]foo.cpp[\i] file where we define the function and **use explicit instantiation**

1
2
3
4
5
6
7
8
9
10
#include "foo.hpp"
#include <iostream>


template <typename T>
void foo(){
    std::cout << "Call to foo \n";
}

template void foo<int>(); //explicit istantiation 



Now in the main.cpp file, the one I want to execute, I have
1
2
3
4
5
6
7
#include "foo.hpp"

int main(){
    
    foo<int>();
    return 0;
}


In order to compile (with -std=c++14 flag), I have to do

 
g++ -o main.x -std=c++14 main.cpp foo.cpp


The question is: **is this the correct way to run compile the main file using explicit instantiation** ?

If I simply do:
 
g++ -o main.x -std=c++14 main.cpp 


we have undefined symbols because the compiler doesn't have access to the implementation of the methods.

Last edited on Jan 21, 2021 at 6:36pm
Jan 21, 2021 at 6:43pm
Use an explicit instantiation declaration in the header file
https://en.cppreference.com/w/cpp/language/function_template#Explicit_instantiation

extern template void foo<int>(); //explicit istantiation declaration

Last edited on Jan 21, 2021 at 6:45pm
Jan 21, 2021 at 9:06pm
I wrote that line in my header foo.hpp, but it's not compiling because a linkage error occurs.

Shouldn't I do the explicit instantiation in the .cpp file, as wrote in the accpeted answer at https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file ?
Jan 22, 2021 at 12:53am
Here is a complete example:

main.cpp
1
2
3
4
5
6
7
template <int N> int foo();
extern template int foo<42>();

int main()
{
  foo<42>();
}


foo.cpp
1
2
template <int N> int foo() { return N; }
template int foo<42>();

http://coliru.stacked-crooked.com/a/6cf7f3df0f971722
Jan 22, 2021 at 8:47am
Thanks @mbozzi for your snippet. Shouldn't you include your foo.cpp in your main? In that case, I understand your snippet works.

But I was concerned with the splitting in header/source and main, as I wrote in my question. Could you please tell me if the way I compile is the correct one? Or what people do in general when they want to do things that way?

To me, it's a bit weird because I need to compile also the source foo.cpp, but maybe this is precisely because of explicit instantiation.
Last edited on Jan 22, 2021 at 8:48am
Jan 22, 2021 at 3:51pm
Sorry @VoB, I should have read your post more carefully.

To compile main.cpp alone you need to pass the -c flag to the compiler, as in
g++ -o main.cpp.o -std=c++14 -c main.cpp
This compiles but does not link main.cpp. The output is an object file, which must later be linked with the other object files into an executable.

main.cpp.o references symbols that are defined elsewhere -- for example, the explicit instantiation
template int foo<42>();
is defined elsewhere, in foo.cpp.o

Therefore foo.cpp must be compiled as well
g++ -o foo.cpp.o -std=c++14 -c foo.cpp
And the two object files linked together
g++ -o main.x main.cpp.o foo.cpp.o

Alternatively, if you want the compiler to manage these details, you can write
g++ -o main.x -std=c++14 main.cpp foo.cpp
to compile and link everything at once. As you've already discovered.
Last edited on Jan 22, 2021 at 3:54pm
Jan 22, 2021 at 4:09pm
Thanks, now I have finally got it: first I create the object codes, and then I link them together.

I also tried to do the same you did here, but with my own example and it works !


However, there's still something unclear to me: shouldn't you include foo.cpp in your main.cpp ? Or did you just overcame this by using extern?
Last edited on Jan 22, 2021 at 4:09pm
Jan 22, 2021 at 4:28pm
However, there's still something unclear to me: shouldn't you include foo.cpp in your main.cpp ? Or did you just overcame this by using extern

In this case, extern is the workaround. It tells the compiler "this function template is explicitly instantiated later, maybe in a different translation unit; do not waste time implicitly instantiating it".

Of course, since the definition of the function template is not available within main.cpp it could not be implicitly instantiated within main.cpp anyway. If we omitted the explicit instantiation declaration we'd get a linker error.

In a conventional build setup, .cpp files are never included. Each .cpp file is considered part of an independent translation unit and compiled separately.
Last edited on Jan 22, 2021 at 4:28pm
Jan 22, 2021 at 4:45pm
Okay, now it's clear to me the purpose of extern.

However, why do you have to write template <int N> int foo(); at the beginning of the file? Why isn't just extern template int foo<42>(); sufficient?

Jan 22, 2021 at 5:52pm
Why isn't just extern template int foo<42>() sufficient

Firstly, there's not enough information in the explicit instantiation declaration alone to determine which template instantiation is being declared.

For example, the primary template could have default template arguments:
1
2
// template <int N, int = 1> int foo(); 
extern template int foo<42>(); 

If the first line was un-commented, the second line would declare an explicit instantiation of
template int foo<42, 1>();

Secondly, and more importantly, unlike generics in other languages, C++ templates are purely a meta-programming facility. They do not exist in the compiled code. They only tell the compiler how to write code for you.

The function template
template <int N> int bar() { std::cout << N << '\n'; }
Exists only at compile-time as an instruction to the compiler. The function template tells the compiler how to write a set of independent functions (incl. template int bar<1>() and template int bar<251>()) for the programmer on demand.

If I try to call bar<2>() in my source code, the compiler must locate the corresponding function template, which is
template <int N> int bar() { std::cout << N << '\n'; }
and substitute N with 10 to produce this actual function
template int bar<10>() { std::cout << 10 << '\n'; }
which can then be called. (This process is called implicit instantiation.)

The compiler must be able to "see" the template's definition to generate code
. This means the template's definition must be visible from the same translation unit which instantiates the template. See also this stack overflow answer:
https://stackoverflow.com/q/495021/2085046
Last edited on Jan 22, 2021 at 5:53pm
Jan 22, 2021 at 6:10pm
I see, thanks so much. Btw, I have just a quick question about another possible way to split the declaration and definition. In particular I'm referring to the accepted answer's solution, the one using a .tpp file.

Namely:

First we have the .tpp file:

1
2
3
4
5
6
7
8
9
10
//foo.tpp file

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


template <typename T>
void foo(){
    std::cout << "Call to foo \n";
}



Then include the .tpp in the .hpp

1
2
3
4
5
6
7
8
9
10
11
12
//foo.hpp file 

#ifndef foo_hpp
#define foo_hpp


template <typename T>
void foo();

#include "foo.tpp" //here we include the .tpp in the .hpp

#endif /* foo_hpp */ 



And the the main is the same as before:

1
2
3
4
5
6
7
#include "foo.hpp"

int main(){
    
    foo<int>();
    return 0;
}



The question is: in this way, since the .tpp files has #include "foo.hpp" and it's included also in the .hpp, isn't like if the .hpp file is self-contained?
Last edited on Jan 22, 2021 at 6:47pm
Jan 22, 2021 at 6:56pm
In this way, the since the .tpp files has #include "foo.hpp" and it's included also in the .hpp, isn't like if the .hpp file is self-contained?

Remember that #include "xyz" means "Paste the contents of xyz here".

Given the header file,
1
2
3
4
5
#ifndef foo_hpp
#define foo_hpp
 //... 
#include "foo.tpp"
#endif 


So when foo.tpp is included the results are
1
2
3
4
5
6
7
8
9
#ifndef foo_hpp
#define foo_hpp
  //... 
#  ifndef foo_hpp
#  define foo_hpp
     //... 
#  include "foo.tpp"
#  endif
#endif 

But the preprocessor conditional on line 4 doesn't expand because foo_hpp is defined already on line 2.

These are "header" or "include" guards:
https://www.learncpp.com/cpp-tutorial/header-guards/
Last edited on Jan 22, 2021 at 6:56pm
Jan 22, 2021 at 7:27pm
Okay, everything is clear now.

I also agree about the thing you wrote and then deleted, i.e. to put both declaration and definition in the header.

Thanks for your time and valuable suggestions :-)
Last edited on Jan 22, 2021 at 7:32pm
Jan 22, 2021 at 10:00pm
Actually, I've just seen something I shouldn't have done.

Look at mine .tpp file: I can avoid to write #include foo.hpp because actually I do not need to compile it, but just to "past" its content in the header foo.hpp.

Indeed, it still compiles, but in this case I have not self-inclusion.

Is everything correct? :-)
Jan 23, 2021 at 11:20pm
I'm really sorry for bothering you, but could you please confirm my last message?

I'd like to be sure about that point.
Jan 24, 2021 at 1:43am
I'm really sorry for bothering you, but could you please confirm my last message?

It's no problem -- I've just been busy.
Is everything correct?

Yes, there's no need to include foo.hpp from foo.tpp.
Jan 24, 2021 at 1:05pm
Thanks for the check !
Topic archived. No new replies allowed.