我一直在开发一个主要是模板函数的库,并设法通过以下方式保持组织(在某种程度上):
// MyLib.h
class MyLib
{
template<class T>
static void Func1()
{
}
template<class T>
static void Func2()
{
}
};
很明显,这样会打电话:
MyLib::Func1();
正如您所看到的,随着更多功能的添加,这会变得非常难看。至少,我想把它分成不同的文件!
我最初考虑在MyLib
命名空间中的单独文件中定义批处理函数,然后使用MyLib.h
来合并所有这些函数,但我不断收到大量链接器错误 - 当然,我可以采取如果建议,仔细看看这种方法。
有什么想法吗?
PS:由于大多数这些函数具有不同的目标,因此将它们分组在我们实例化对象的类下是没有意义的。我在这里使用了class
所以我不必担心我定义函数的顺序(MyLib
中的函数之间也存在相互依赖关系。)
链接器错误:
所以基本结构是这样的:我有两个类(比如A&amp; B)编译到静态库和一个运行这些类的实例的主应用程序。这些课程A&amp; B使用MyLib
中的函数。当A&amp; B正在编译我收到LNK4006
警告,该警告表明属于MyLib
的符号已经在项目中的 OBJ 文件中定义,并且忽略了它。
当涉及到应用程序时,它会变成LNK2005
错误,表明它已经在A&amp; A的 OBJ 文件中定义。乙
更新 谢谢Mike&amp;对于内联的想法,Mathieu就是问题!
除了一个问题:我有一些我明确专门化的模板函数,这些函数导致already defined
错误(LNK2005
):
template<class t> int Cvt(){}
template<> int Cvt<unsigned char>(){return 1;}
template<> int Cvt<char>(){return 2;}
template<> int Cvt<unsigned short>(){return 3;}
有什么想法吗?
Conlusion:
通过在单独的文件中定义模板函数解决了显式特化问题 - 感谢您的帮助!
答案 0 :(得分:5)
您应该更喜欢使用静态方法在您的类上使用命名空间:
using myNamespace::MyFunc;
启动(注意:编写using myNamespace;
是不好的做法,你应该避免这种做法)现在,我们来谈谈组织:
Hello, World!
而包含整个世界,而商品标题可以提供帮助(即,标题可以帮助一堆包括懒惰的程序员使用)[1]这就是我的意思:
#include "lib/string/manip.hpp" // Okay, this files come from "lib"
int main(int argc, char* argv[])
{
std::string s;
lib::string::manip(s); // Same hierarchy, easy to remember the header
return 0;
}
一个激励的例子? Boost做到了(带有商品标题)。
而且这不会花费太多:只需将class
替换为namespace
并删除static
个关键字,这就是所有人。
对于链接器问题:所有未模板化的方法都应声明为inline
(除非它们是单行,否则尝试避免它)或在标题之外定义(在单独的{{ 1}}文件)。
<强>更新强>
模板特化的问题在于你最终定义了一个现在的“普通”方法:一旦你修复了每个参数,就不再有关于它的模板了。因此,解决方案就像您对正常函数所做的那样:在头文件中声明和在源文件中定义(因此只有一次)。
更具体地说明这个奇怪的错误:C ++的问题是每个源文件都是独立编译的:预处理器将采用include并实际创建一个包含每个包含文件的文本文件(在订单)然后你的来源在最后。编译器获取此文件并生成“.o”文件(对于gcc)。然后链接器启动并尝试从所有这些“.o”文件中实际创建库(或二进制文件),并检查每个方法是否只定义一次,否则它将如何在多个定义之间进行选择(遗憾的是不会检查它们是否相同......)?
虽然模板方法和类有一个特殊的限制,它在所有实例化中(每个模板参数组合的一个实例)中选取一个(随机)。当然,这假设所有这些都是相同的,你最终会因为类似的事情而头疼:
.cpp
两条线都会打印相同的输出,不管它是10还是20都是未知的,并且可以在构建之间改变!!!
答案 1 :(得分:4)
MyLib名称空间是显而易见的方式 - 毕竟,这基本上是标准库,它可能比你的大得多。使用模板获得大量链接器错误是不常见的,除非你有很多前向声明 - 你通常应该尽量避免这种情况。
答案 2 :(得分:3)
使用命名空间正确的方法。就个人而言,我不会将它们合并为一个“包括整个世界”标题,因为这会增加编译时间。其他人可能更喜欢单个标题的便利性。
如果存在任何非模板函数,则必须将它们声明为inline
,或者仅在一个源文件中实现。模板函数和类定义中实现的类成员函数隐式inline
,但其他函数不是。{/ p>