【问题标题】:Separate definitions of variadic templates可变参数模板的单独定义
【发布时间】:2015-07-11 01:42:22
【问题描述】:

(最后的问题在底部)

最近,我向 a question 询问了我将如何修复链接器错误(关于与模板 void 的多个定义有关的重复符号。

因为我在多个源文件中使用了函数,所以建议我使用关键字inline 来允许在头文件中声明或将声明放在已编译的源文件中。

在我意识到inline 有一些不好的影响后,我将我的声明放在了一个源文件中。

现在这没问题了,除了可变参数模板:

template<typename T, typename... Args>
void cleanup(T *t, Args&&... args);

我找到了一些明显的解决方案 - 但不是可变参数模板 - 使用 .tpp 文件(但它再次开始声明重复符号)或保留源文件并添加显式实例化。

但是void cleanup 有可能使用数百种参数组合,所以我不想显式实例化所有内容。

问题: 那么,我该怎么做呢

  1. 将可变参数模板定义保留在源文件中,或者
  2. 将定义放入 .tpp 文件中不会出现重复符号,最终避免使用inline?

.tpp 声明的重复/未定义符号错误示例并将上述模板定义分别放在源文件中。

duplicate symbol __Z7cleanupI10SDL_WindowJEEvPT_DpOT0_ in:
    CMakeFiles/Game.dir/Game/main.cc.o
    CMakeFiles/Game.dir/Game/RichTools/rtexture.cc.o

_

Undefined symbols for architecture x86_64:
  "void cleanup<SDL_Renderer, SDL_Window*&>(SDL_Renderer*, SDL_Window*&&&)", 
referenced from:
cleanQuit() in main.cpp.o
ld: symbol(s) not found for architecture x86_64

【问题讨论】:

  • 您希望编译器在使用时生成实例化,但又想自动丢弃重复项?那就用inline吧!您提到您考虑过使用它,但它有“不良影响”,但究竟是什么?
  • 似乎在您的第一个问题中您没有理解答案。进行内联或移至源文件的建议仅与您完全专业化的功能有关-这些带有空参数列表模板。

标签: c++ templates variadic-templates undefined-symbol


【解决方案1】:

回答您的第一个问题的建议:进行内联或移动到源文件仅与您完全专业化的功能有关 - 这些带有空参数列表template &lt;&gt;

那么,就这样吧:

你的头文件:

// This shall be in header - it is not full specialization:
template<typename T, typename... Args>
void cleanup(T *t, Args&&... args){
    //Cleanup the first item in the list
    cleanup(t);
    //Recurse to clean up the remaining arguments
    cleanup(std::forward<Args>(args)...);
}
// These shall be only declared here, and implemented in source file, 
// treat fully specialized function templates as regular functions
template<>
void cleanup<SDL_Window>(SDL_Window *win);
template<>
void cleanup<SDL_Renderer>(SDL_Renderer *ren);
template<>
void cleanup<SDL_Texture>(SDL_Texture *tex);

您的源文件:

template<>
void cleanup<SDL_Window>(SDL_Window *win){
    if (!win){
        return;
    }
    SDL_DestroyWindow(win);
}
template<>
void cleanup<SDL_Renderer>(SDL_Renderer *ren){
    if (!ren){ 
        return;
    }
    SDL_DestroyRenderer(ren);
}
template<>
void cleanup<SDL_Texture>(SDL_Texture *tex){
    if (!tex){
        return;
    }
    SDL_DestroyTexture(tex);
}
template<>
void cleanup<SDL_Surface>(SDL_Surface *surf){
    if (!surf){
        return;
    }
    SDL_FreeSurface(surf);
}

【讨论】:

  • 但这意味着我必须将内联用于“非专用”功能,就像我之前尝试过的那样。
  • @Hyperum 你为什么这么认为?试试我的建议。对于所有不完全专业化的函数 - 只是它们必须在头文件中,它们可以是内联的或非内联的。 C++ 链接器会自行删除此类重复项 - 您无需担心。
  • @Hyperium - 还有明确的模板实例化方式 - 但正如您已经观察到的 - 使用起来非常不方便......
  • 好吧,好吧,我想它成功了。现在,我很困惑,因为我以前曾尝试过这种方式,但我不得不使用关键字inline,因为它给了我重复的符号——所以我一定做错了什么。无论如何,谢谢。
  • @Hyperum 你的链接器只抱怨完全专用的函数:_ZN5RUtil7cleanupI10SDL_WindowJEEEvPT_DpOT0void RUtil::cleanup&lt;SDL_Window&gt;(SDL_Window*)。使用 demangler.com 获取链接器符号的解构版本...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多