c++ - 使用(mock)头文件作为模板时的“Multiple definition”

标签 c++ templates codeblocks header-files

我知道必须将C++模板化函数的定义放在头文件中。但是,由于提高了我正在制作的(可能是)大库的可读性和结构的原因,我将声明与实现分开,放入“模拟” header (该#include实现文件,与文件的this structure类似)。请注意,我知道模板功能的实现必须在编译时包括在中,而我正在这样做。

简而言之,将非模板函数声明添加到实现文件中时,出现“多个定义”错误。以下是带有示例的详细说明。

当一对“模拟”头文件+实现文件仅包含模板函数的声明/实现对时,一切正常。当我仅在实现文件中添加新模板功能的实现时,它也可以正常工作。

工作示例(当我想使用此功能时,可以在#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

我收到了“多个定义” 错误(从我在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

为什么这仅会发生在非模板函数上,而对于模板化也可以正常工作,更重要的是,我如何解决此问题?

我到处都是,但找不到有用的东西:(理想情况下,我正在寻找尽可能接近自己的文件结构的东西(但是我会采取一切可行的措施,同时仍将某些部分分隔为“模拟” “.h + tpp/.cpp)。我是否必须将其他子功能分解为单独的,没有模板的一对.h/.cpp文件,还是有其他解决方案?(理想情况下,最终用户应该看不到这些子功能) )。

我不愿意在定义inline时使用fl::moreSubFunc(..),因为该函数相当大(而且我被告知inline理想情况下仅应与小型函数一起使用)。确实可以解决问题,但是我正在寻找是否有其他解决方案。

我正在使用Code::Blocksgcc version 4.7.2中工作。这是我的实现文件是tpp/.cpp(扩展名.cpp)的最初原因,因为Code::Blocks默认情况下不支持它。在@πάντα ῥεῖ的建议下(在下面看),在当前实现中对此进行了更改。

后期编辑(在我教了解决方案之后,我教了@πάντα ῥεῖ's answer解决了这个问题。我调整了Code::Blocks以接受.tpp文件(将其视为头文件或源文件)。最初,此解决方案有效。

但是,此解决方案仅在algo.h文件仅包含在另一个文件中时有效:当我仅在main.cpp中包含它时。但是,一旦我尝试将其包含在将使用这些算法(除main.cpp之外)的另一个源文件(例如algo2.cpp)中,多定义问题又回来了。

底线,只要我将algo.h包含在多个文件中,问题仍然存在,我仍在寻找解决方案。

最佳答案

发生您的问题的原因是,链接时的功能模板与“普通”免费功能的处理方式不同。函数必须遵循One Definition Rule (ODR);也就是说,它们的定义不得超过一个翻译单位。否则,当您链接时间时,最终会遇到多个定义错误,例如您引用的错误。相同的规则通常也适用于类,类型和对象。

这似乎完全排除了模板的使用;它们必须完全包含在使用它们的每个翻译单元中。但是,ODR在某些情况下是一个异常(exception)。引用维基百科:

Some things, like types, templates, and extern inline functions, can be defined in more than one translation unit. For a given entity, each definition must be the same. Non-extern objects and functions in different translation units are different entities, even if their names and types are the same.



这就是为什么您不会在模板函数中遇到多个定义错误的原因。在链接时,链接程序会找到重复的符号定义,并删除所有重复的符号(只要它们都相等即可;否则,将是错误的)。因此,您的程序将成功链接每个所需符号的确切定义。

对于您的情况,发生问题是因为您在多个翻译单元(包括.cpp文件的所有位置)中都包含非模板函数。有几种解决方法:
  • 如果模板函数是类的一部分,则可以将非模板函数也移到该类中。这将使其归入拥有模板类的重复数据删除功能。
  • 将功能标记为内联。
  • 将非模板函数分解为另一个.cpp文件,然后分别编译。那将是容纳它们的唯一翻译单位。
  • 关于c++ - 使用(mock)头文件作为模板时的“Multiple definition”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25139535/

    相关文章:

    c++ - 向 CMake 引入新的编译器

    c++ - 如何将 C++ 位集中的位范围子集转换为数字?

    c++ - C++ 中的幂函数

    c++ - 匹配自定义仿函数或 stod、stoi 等的模板

    c++ - MSVC std::pair 实现:SFINAE 是否在此处正确应用?

    c++ - gotoXY 显示在屏幕上

    c++ - 如何从文件输入然后在 C++ 中同时使用标准 i/o 流?

    C++ 模板数组运算符 [] 使用整数

    c++ - SFML loadFromFile undefined reference (测试有效,但添加图像时崩溃)

    c++ - 使用代码块在 Windows 7 上运行 TBB 代码