草庐IT

C++模板函数在 header 中编译但未在实现中编译

coder 2023-05-03 原文

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

这是调用 header 的代码(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 移动到 .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 文件中,就像您之前所做的那样。这有利有弊,包括解决问题(pro),可能使代码的可读性降低以及在某些编译器上更难调试(con),并且可能会增加代码膨胀(con)。

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

文件: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 hpp h 中的文件文件:

文件: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 按值。您可能不想这样做,而是通过引用或指针传递它,或者更好的是,传递迭代器以避免临时生成并复制整个 vector 。

关于C++模板函数在 header 中编译但未在实现中编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3040480/

有关C++模板函数在 header 中编译但未在实现中编译的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  3. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  4. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  5. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  6. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  7. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  8. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  9. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  10. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

随机推荐