我知道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(..)
理想情况下应该只用于小函数)。这确实解决了问题,但我正在寻找是否有不同的解决方案。
我使用inline
在Code::Blocks
工作。这是我的实施文件gcc version 4.7.2
(tpp/.cpp
扩展名)的最初原因,因为.cpp
默认情况下不支持它。在@πάντα ῥεῖ的建议(见下文)之后的当前实施中,这已经改变。
延迟编辑(在我发现解决方案之后),我教@πάντα ῥεῖ's answer解决问题。我调整了Code::Blocks
来接受Code::Blocks
个文件(将其视为标题或源文件)。最初,这个解决方案有效。
然而,此解决方案仅在 algo.h 文件仅包含在另一个文件中时才起作用:当我仅将其包含在 main.cpp <时/ em>的。但是,只要我尝试将其包含在另一个使用这些算法的源文件(例如 algo2.cpp )中(除 main.cpp 之外),多重定义问题又回来了。
一句话,一旦我将 algo.h 包含在多个文件 中,问题仍然存在,我仍然在寻找解决方案
答案 0 :(得分:2)
出现问题是因为功能模板在链接时的处理方式不同于&#34; plain&#34;免费功能。函数必须服从One Definition Rule (ODR);也就是说,它们必须在不超过一个翻译单元中定义。否则,当您获得链接时间时,最终会出现多个定义错误,例如您引用的错误。同样的规则也适用于类,类型和对象。
这似乎完全排除了模板的使用;它们必须完全包含在使用它们的每个翻译单元中。但是,ODR在少数情况下例外。引自维基百科:
可以在多个翻译单元中定义某些内容,例如类型,模板和外部内联函数。对于给定的实体,每个定义必须相同。不同翻译单元中的非外部对象和函数是不同的实体,即使它们的名称和类型相同。
这就是您不会使用模板函数遇到多个定义错误的原因。在链接时,链接器找到重复的符号定义并删除所有重复项(只要它们都是等效的;否则,这将是一个错误)。因此,您的程序可以成功链接每个所需符号的一个定义。
对于您的情况,出现问题的原因是您在多个翻译单元中包含非模板函数(包含.cpp
文件的任何位置)。有几种方法可以解决这个问题:
如果模板函数是类的一部分,则可以将非模板函数移动到该类中。这将把它带到拥有模板类的符号重复数据删除伞下。
将函数标记为内联。
将非模板函数分解为另一个.cpp
文件,然后单独编译。这将是唯一容纳他们的翻译单位。
答案 1 :(得分:0)
作为一个好的经验法则:
“永远不要包含
.cpp
文件。”
您不应该为这些模板实施文件提供.cpp
扩展名。您的构建系统可能会自动包含它们。
此类文件的常规使用扩展名例如是.tcc
或.icc
。将这些添加到项目中,就像添加其他头文件一样
不要将它们作为单独构建和链接的翻译单元包含在项目中,如果它们与另一个头文件中的#include
语句一起使用。
<强>更新强>
正如您特别要求使用代码块一样,您只需稍微调整一下文件扩展名设置,就可以正确地将这些文件包含在项目中,并像往常一样使用它们的语法颜色:
1. 将新文件类型添加到文件扩展名设置中
2. 将文件类型添加到项目文件扩展名设置Project->Project Tree->Edit file types & categories
一旦.tcc
文件立即添加到项目中,它将使用文本编辑器打开,并且语法照常着色:
相应的.hpp
文件如下所示
答案 2 :(得分:0)
通常,您应该单独编译.cpp文件并在以后链接它。但如果您想(或必须)这样做,请将所有非模板函数标记为inline
。这应该有所帮助。