Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

This problem is in defining and declaring a function template in a namespace that is defined in an external file from where the function is instantiated. Here's the smallest reproducible example I could come up with. 4 files follow:

The function template declaration in a named namespace:

// bar.h
#include <algorithm>

namespace barspace {

  template <typename Iter>
    void DoSomething (Iter first, Iter last);

}

The 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

The header for the main program

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

Lastly, 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 compile as follows:

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

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

These run fine. Now for linking:

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

And the resulting compiler error about the undefined reference:

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 code not getting made available from within the namespace, or from the bar compilation unit.

Furthermore, when I try to place the definition of DoSomething in the bar.h header, as I would to circumvent the issues when defining class template methods in separate .cpp files, I get the same error.

Can you shed some light unto my compiler linking error?

like image 939
Shamster Avatar asked Jul 16 '10 00:07

Shamster


2 Answers

You are trying to hide the implementation of your templated function into the cpp file, which, unfortunately, is not possible for most compilers. Templated functions/classes are instantiated when used, so at the point where you are calling DoSomething, the compiler needs the definition of the function to be able to compile it.

There are a couple of solutions.

  1. Move the function body into the header file. You had trouble doing that before, but i'd say it's related to something else. This is the preferred approach.

  2. Include the cpp file from foo.cpp. (wild, but not that uncommon).

  3. Instantiate the 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
like image 175
Igor Zevaka Avatar answered Oct 05 '22 21:10

Igor Zevaka


Putting templates definitions in a separate source file is not well supported (I believe the only compiler that support that is Comeau).

You need to move the definition into your 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
like image 29
R Samuel Klatchko Avatar answered Oct 05 '22 19:10

R Samuel Klatchko