C++ 模板函数在头文件中编译,但在实现中不编译

2024-02-07

我正在尝试学习模板,但遇到了这个令人困惑的错误。我在头文件中声明了一些函数,并且我想创建一个单独的实现文件来定义这些函数。

这是调用标头的代码(dum.cpp):

#include <iostream>
#include <vector>
#include <string>
#include "dumper2.h"

int main() {
    std::vector<int> v;
    for (int i=0; i<10; i++) {
        v.push_back(i);
    }
    test();
    std::string s = ", ";
    dumpVector(v,s);
}

现在,这是一个工作头文件(dumper2.h):

#include <iostream>
#include <string>
#include <vector>

void test();

template <class T> void dumpVector(const std::vector<T>& v,std::string sep);

template <class T> void dumpVector(const std::vector<T>& v, std::string sep) {
    typename std::vector<T>::iterator vi;

    vi = v.cbegin();
    std::cout << *vi;
    vi++;
    for (;vi<v.cend();vi++) {
        std::cout << sep << *vi ;
    }
    std::cout << "\n";
    return;
}

随着实施(dumper2.cpp):

#include <iostream>
#include "dumper2.h"

void test() {
    std::cout << "!olleh dlrow\n";
}

奇怪的是,如果我移动定义的代码dumpVector来自.h to the .cpp文件,我收到以下错误:

g++ -c dumper2.cpp -Wall -Wno-deprecated
g++ dum.cpp -o dum dumper2.o -Wall -Wno-deprecated
/tmp/ccKD2e3G.o: In function `main':
dum.cpp:(.text+0xce): undefined reference to `void dumpVector<int>(std::vector<int, std::allocator<int> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
collect2: ld returned 1 exit status
make: *** [dum] Error 1

那么为什么它以一种方式起作用而不是以另一种方式起作用呢?显然编译器可以找到test(),那为什么找不到dumpVector?


您遇到的问题是编译器不知道要实例化模板的哪个版本。当您将函数的实现移至 x.cpp 时,它位于与 main.cpp 不同的翻译单元中,并且 main.cpp 无法链接到特定实例化,因为它不存在于该上下文中。这是 C++ 模板的一个众所周知的问题。有以下几种解决方案:

1) 只需将定义直接放入 .h 文件中,就像之前所做的那样。这有优点和缺点,包括解决问题(优点),可能使代码可读性较差,在某些编译器上更难调试(缺点),并且可能增加代码膨胀(缺点)。

2)将实现放在x.cpp中,并且#include "x.cpp"从内部x.h。如果这看起来很时髦并且错误,请记住#include只是读取指定的文件并编译它就好像该文件是x.cpp换句话说,这与上面的解决方案#1 的作用完全相同,但它将它们保留在单独的物理文件中。在做这种事情时,重要的是不要尝试编译#included 文件是它自己的。出于这个原因,我通常会给这些类型的文件一个hpp扩展名以区分它们h文件和来自cpp files.

文件:dumper2.h

#include <iostream>
#include <string>
#include <vector>

void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);
#include "dumper2.hpp"

文件:dumper2.hpp

template <class T> void dumpVector(std::vector<T> v, std::string sep) {
  typename std::vector<T>::iterator vi;

  vi = v.begin();
  std::cout << *vi;
  vi++;
  for (;vi<v.end();vi++) {
    std::cout << sep << *vi ;
  }
  std::cout << "\n";
  return;

}

3)由于问题在于特定的实例化dumpVector对于尝试使用它的翻译单元来说是未知的,您可以在定义模板的同一翻译单元中强制对其进行特定实例化。只需添加以下内容即可:template void dumpVector<int>(std::vector<int> v, std::string sep);...到定义模板的文件。这样做,您不再需要#include the hpp文件中的h file:

文件:dumper2.h

#include <iostream>
#include <string>
#include <vector>

void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);

文件:dumper2.cpp

template <class T> void dumpVector(std::vector<T> v, std::string sep) {
  typename std::vector<T>::iterator vi;

  vi = v.begin();
  std::cout << *vi;
  vi++;
  for (;vi<v.end();vi++) {
    std::cout << sep << *vi ;
  }
  std::cout << "\n";
  return;
}

template void dumpVector<int>(std::vector<int> v, std::string sep);

顺便说一句,总的来说,您的模板函数正在使用vector by-value。您可能不想这样做,并通过引用或指针传递它,或者更好的是,传递迭代器以避免制作临时和复制整个向量。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 模板函数在头文件中编译,但在实现中不编译 的相关文章

随机推荐