"多重定义"使用(模拟)头文件的模板时

时间:2014-08-05 13:02:13

标签: c++ templates codeblocks header-files

我知道C ++模板化函数的定义必须放在头文件中。但是,出于提高可读性的原因以及我正在制作的(可能)大型库的结构,我将声明与实现分离,进入" mock"标题#include实施文件,非常类似this structure个文件)。 请注意,我知道在编译时必须包含模板化函数的实现,我正在做

简而言之,我有一个"多重定义"将非模板化函数声明添加到实现文件时出错。下面将举例说明。

当这对"模拟" header + implementation files 只包含模板化函数的声明/实现对,一切正常。当我只在实现文件中添加新的模板化函数的实现时,它也可以正常工作。

工作示例(当我想使用此功能时,我会在#include "algo.h"main.cpp

"模拟"头文件 algo.h

#ifndef ALGO_H
#define ALGO_H

namespace fl{
    template <typename Compare>
    void algo(.. non-templated args .., Compare order = std::less<int>());
}

#include "tpp/algo.cpp"

#endif // ALGO_H

实施文件 tpp / algo.cpp (目前只是 algo.tpp
注意:使用tpp/.cpp文件位于初始版本中,现在我按照@πάντα ῥεῖ的建议使用.tpp文件,并在端。

#ifndef TPP_ALGO
#define TPP_ALGO

#include "../algo.h"

namespace fl{

    template <typename Compare>
    void subFunctionality(Compare order, .. args ..){ /* impl */ }

    template <typename Compare>
    void algo(.. non-templated args .., Compare order){
         subFunctionality(order, .. args ..);
        // implementation
    }
}

#endif // TPP_ALGO

当我在实现文件中添加非模板化函数实现时,问题出现了 tpp / algo.cpp (目前只是 algo.tpp 的(非工作)示例(使用相同的 algo.h ):

#ifndef TPP_ALGO
#define TPP_ALGO

#include "../algo.h"

namespace fl{

    template <typename Compare>
    void subFunctionality(Compare order, .. args ..){ /* impl */ }

    void moreSubFun(.. args ..) { /* impl */ }

    template <typename Compare>
    void algo( .. non-templated args ..., Compare order){
         subFunctionality(order, .. args ..);
         moreSubFun(.. args ..);
         // more stuff
    }
}

#endif // TPP_ALGO

我收到&#34;多重定义&#34; 错误(我将其包含在main.cpp中),如下所示:

obj/Release/main.o                 In function `fl::moreSubFun(...)':
main.cpp                           multiple definitions of `fl::moreSubFun(..)'
obj/Release/../tpp/algo.o:algo.cpp first defined here

为什么这只发生在非模板化函数上,而它适用于模板化,更重要的是,如何解决这个问题?

我四处寻找,我找不到任何有用的东西:(理想情况下,我正在寻找尽可能接近我自己的文件结构的东西(但我会采取任何有效的方法)仍然使用一些分离成&#34; mock&#34; .h + tpp/.cpp)。我是否必须将其他子功能分解为一个单独的,非模板化的{{1}对文件,还是有其他解决方案?(理想情况下,子功能对最终用户不可见)。

我在定义.h/.cpp时不愿意使用inline,因为函数非常大(我被教导fl::moreSubFunc(..)理想情况下应该只用于小函数)。这确实解决了问题,但我正在寻找是否有不同的解决方案。

我使用inlineCode::Blocks工作。这是我的实施文件gcc version 4.7.2tpp/.cpp扩展名)的最初原因,因为.cpp默认情况下不支持它。在@πάντα ῥεῖ的建议(见下文)之后的当前实施中,这已经改变。


延迟编辑(在我发现解决方案之后),我教@πάντα ῥεῖ's answer解决问题。我调整了Code::Blocks来接受Code::Blocks个文件(将其视为标题文件)。最初,这个解决方案有效。

然而,此解决方案仅在 algo.h 文件仅包含在另一个文件中时才起作用:当我仅将其包含在 main.cpp <时/ em>的。但是,只要我尝试将其包含在另一个使用这些算法的源文件(例如 algo2.cpp )中(除 main.cpp 之外),多重定义问题又回来了。

一句话,一旦我将 algo.h 包含在多个文件 中,问题仍然存在,我仍然在寻找解决方案

3 个答案:

答案 0 :(得分:2)

出现问题是因为功能模板在链接时的处理方式不同于&#34; plain&#34;免费功能。函数必须服从One Definition Rule (ODR);也就是说,它们必须在不超过一个翻译单元中定义。否则,当您获得链接时间时,最终会出现多个定义错误,例如您引用的错误。同样的规则也适用于类,类型和对象。

这似乎完全排除了模板的使用;它们必须完全包含在使用它们的每个翻译单元中。但是,ODR在少数情况下例外。引自维基百科:

  

可以在多个翻译单元中定义某些内容,例如类型,模板和外部内联函数。对于给定的实体,每个定义必须相同。不同翻译单元中的非外部对象和函数是不同的实体,即使它们的名称和类型相同。

这就是您不会使用模板函数遇到多个定义错误的原因。在链接时,链接器找到重复的符号定义并删除所有重复项(只要它们都是等效的;否则,这将是一个错误)。因此,您的程序可以成功链接每个所需符号的一个定义。

对于您的情况,出现问题的原因是您在多个翻译单元中包含非模板函数(包含.cpp文件的任何位置)。有几种方法可以解决这个问题:

  1. 如果模板函数是类的一部分,则可以将非模板函数移动到该类中。这将把它带到拥有模板类的符号重复数据删除伞下。

  2. 将函数标记为内联。

  3. 将非模板函数分解为另一个.cpp文件,然后单独编译。这将是唯一容纳他们的翻译单位。

答案 1 :(得分:0)

作为一个好的经验法则:

  

“永远不要包含.cpp文件。”

您不应该为这些模板实施文件提供.cpp扩展名。您的构建系统可能会自动包含它们。

此类文件的常规使用扩展名例如是.tcc.icc。将这些添加到项目中,就像添加其他头文件一样 不要将它们作为单独构建和链接的翻译单元包含在项目中,如果它们与另一个头文件中的#include语句一起使用。

<强>更新
正如您特别要求使用代码块一样,您只需稍微调整一下文件扩展名设置,就可以正确地将这些文件包含在项目中,并像往常一样使用它们的语法颜色:

1. 将新文件类型添加到文件扩展名设置中 enter image description here

2. 将文件类型添加到项目文件扩展名设置Project->Project Tree->Edit file types & categories
enter image description here

一旦.tcc文件立即添加到项目中,它将使用文本编辑器打开,并且语法照常着色:
enter image description here

相应的.hpp文件如下所示 enter image description here

答案 2 :(得分:0)

通常,您应该单独编译.cpp文件并在以后链接它。但如果您想(或必须)这样做,请将所有非模板函数标记为inline。这应该有所帮助。