The function template in the namespace in a separate file compiles fine, but the linker cannot find it

This problem is the definition and declaration of the function template in the namespace, which is defined in the external file where this function is created from. Here is the smallest reproducible example that I could come up with. 4 files:

Declaring a function template in a named namespace:

// bar.h #include <algorithm> namespace barspace { template <typename Iter> void DoSomething (Iter first, Iter last); } 

Function template definition in a separate file:

 // bar.cpp #include "bar.h" namespace barspace { template <typename Iter> void DoSomething (Iter first, Iter last) { typedef typename std::iterator_traits<Iter>::value_type val_t; std::sort (first, last); } } // namespace barspace 

Main program title

 // foo.h #include "bar.h" #include <vector> 

Finally, the main program where the function template is called:

 //foo.cpp #include "foo.h" int main () { std::vector<double> v_d; for (int i = 0; i < 10; i++) { v_d.push_back (i); } barspace::DoSomething (v_d.begin(), v_d.end()); return 0; } 

I am compiling the following:

g++ -c -o bar.o bar.cpp

g++ -c -o foo.o foo.cpp

They work fine. Now for the link:

g++ bar.o foo.o -o foobar

And the resulting compiler error regarding the undefined link:

 foo.o: In function `main': foo.cpp:(.text+0x6e): undefined reference to `void barspace::DoSomething<__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > > >(__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >, __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >)' collect2: ld returned 1 exit status 

There is an obvious problem with the fact that the code does not become accessible from namespace or from the compilation unit bar .

Also, when I try to put the DoSomething definition in the bar.h header, since I would get around problems when defining class template methods in separate .cpp files, I get the same error.

Can you shed some light on my compiler's communication error?

+4
source share
4 answers

You are trying to hide the implementation of your templated function in a cpp file, which, unfortunately, is not possible for most compilers. Template functions / classes are created when used, so at the point where you call DoSomething , the compiler needs to define a function so that it can compile it.

There are several solutions.

  • Move the function body to the header file. You had problems with this before, but I would say that this is due to something else. This is the preferred approach.

  • Include the cpp file from foo.cpp . (wild, but not that unusual).

  • Create a template for double :

 // bar.cpp #include "bar.h" namespace barspace { template<> void DoSomething<double> (double first, double last) { typedef typename std::iterator_traits<double>::value_type val_t; std::sort (first, last); } } // namespace barspace 
+7
source

Putting template definitions into a separate source file is not supported (I believe that the only compiler that supports Como).

You need to move the definition to the header files:

 // bar.h namespace barspace { template <typename Iter> void DoSomething (Iter first, Iter last) { typedef typename std::iterator_traits<Iter>::value_type val_t; std::sort (first, last); } } // namespace barspace 
+1
source

Many compilers do not like the template functions defined in separate files. Put the full definition in the header file and it should compile just fine.

+1
source

You must define the template functions in the headers. You cannot declare them and then define them in the implementation file; which just won't work. The reason is that the compiler must create an instance of the templates before you can call them. To create an instance of a function template, the compiler needs two things:

  • Template definition
  • Template options for instantiating using

In your case, the compiler works with two compilation modules. It can run them in two different processes, so the compilation units are independent of each other. When the compiler compiles bar.cpp, it sees the definition of the template without requests (calls) for specific instances. Therefore, the compiler does not instantiate the template; compiling bar.cpp yields essentially nothing. The compiler is smart enough to make sure you don't need this template, and optimizes it to zero.

Of course, you need this template - in foo.cpp - but this is another compilation unit, and by now the compiler has forgotten everything (or hasn’t found out yet) about bar.cpp. However, the compiler sees the declaration of the template function and makes the usual assumption that if it was declared, it was defined (instantiated) in another place and nothing was said.

Finally, the layout comes and gets the final look of the birds. He can see that there is no DoSomething<Iter>(Iter, Iter) created for std::vector<double>::iterator and complains.

There are several solutions to your problem. The best solution is to use the export keyword when declaring a template. Unfortunately, this is also the worst solution, since the vast majority of compilers do not support this standard function.

Seriously, but it's best to define a template in the header file. Do not declare it there, define . You will not need bar.cpp if it was in the first place.

+1
source

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


All Articles