单独文件中命名空间中的函数模板编译良好,但链接器无法找到它

Sha*_*ter 7 c++ linker templates header

此问题是在名称空间中定义和声明函数模板,该名称空间在实例化函数的外部文件中定义.这是我能想出的最小的可重复的例子.4个文件如下:

命名空间中的函数模板声明:

// bar.h
#include <algorithm>

namespace barspace {

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

}
Run Code Online (Sandbox Code Playgroud)

功能模板定义在单独的文件中:

// 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
Run Code Online (Sandbox Code Playgroud)

主程序的标题

// foo.h
#include "bar.h"
#include <vector>
Run Code Online (Sandbox Code Playgroud)

最后,调用函数模板的主程序:

//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;
}
Run Code Online (Sandbox Code Playgroud)

我编译如下:

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

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

这些运行正常.现在链接:

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

以及关于未定义引用的结果编译器错误:

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
Run Code Online (Sandbox Code Playgroud)

代码无法namespacebar编译单元或编译单元中获得,这是一个明显的问题.

此外,当我尝试DoSomethingbar.h标题中的定义放在标题中时,因为我会在单独的.cpp文件中定义类模板方法时避免出现问题,因此我得到了相同的错误.

你能否解释一下我的编译器链接错误?

Igo*_*aka 10

您试图将模板化函数的实现隐藏到cpp文件中,遗憾的是,对于大多数编译器来说,这是不可能的.模板化的函数/类在使用时被实例化,因此在您调用时DoSomething,编译器需要函数的定义才能编译它.

有几种解决方案.

  1. 将函数体移动到头文件中.你以前做过这件事很麻烦,但我会说这与其他事情有关.这是首选方法.

  2. 包括cpp文件foo.cpp.(狂野,但不是那么罕见).

  3. 实例化模板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
Run Code Online (Sandbox Code Playgroud)