2010-01-22 59 views
2

我一直在开发的主要是模板函数库和管理,以保持组织(在某种程度上)东西,在下列方式:创建的模板函数库

// MyLib.h 
class MyLib 
{ 
    template<class T> 
    static void Func1() 
    { 
    } 

    template<class T> 
    static void Func2() 
    { 
    } 
}; 

而且显然要愿意作出这个:

MyLib::Func1(); 

正如你所看到的,随着更多功能的添加,这可能会变得非常难看。至少,我想将它分成不同的文件!

我最初认为定义在MyLib命名空间在不同的文件功能的批次,然后用MyLib.h巩固所有的人,但我一直得到了链接错误的卡车 - 当然,我可以把这个方法,如果仔细看这是建议。

有什么想法?

PS:由于大多数这些函数有不同的目标,所以将它们归类到我们实例化对象的类下是没有意义的。我在这里使用了一个class,所以我不必担心我定义函数的顺序(在MyLib之间也有相互依赖的函数)。

链接器错误:

所以基本结构是这样的:我有两个类(说& B)编译为静态库和运行这些类的实例的主应用。这些类A & B使用MyLib中的函数。当A & B正在编译时,我得到LNK4006警告,其中说明属于MyLib的符号已经在项目中的OBJ文件中定义,并且它忽略了它。

当它归结到它成为一个LNK2005错误,指出它是在一个& B.

更新OBJ文件中已经定义的应用程序: 谢谢Mike & 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

这是正确的做法。就个人而言,我不会将它们合并为一个“包含全世界”标题,因为这样会增加编译时间。这样做不应该有链接器错误 - 你能举一个例子或两个? – 2010-01-22 13:56:27

+0

更新为链接器错误 – Jacob 2010-01-22 15:53:21

+0

我们必须查看链接器错误和相关功能来诊断问题。你是否在库中创建命名对象?一般来说,你不应该那样做。 – 2010-01-22 16:26:07

回答

5

你应该更喜欢的命名空间在你的类静态方法:

  • 命名空间为您提供的可能性,在几个文件共享,每个方法的逻辑组一个
  • 命名空间可以被省略:或者是因为ADL踢或using myNamespace::MyFunc;(注:这是不好的做法写using myNamespace;,你应该回避的做法)

现在,让我们说话的组织:

  • 这是很好的做法,有你的文件层次阴影的命名空间层次结构[1]
  • 它通过逻辑组来分割你的方法,使用户不必仅仅包括因为他希望整个世界的良好做法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; 
} 

一个激励的例子?提升它(商品标题)。

更重要的是,这并没有多少成本:只需将class替换为namespace并删除static关键字,这就是所有人。

对于连接问题:未模板化应该是声明为inline(尽量避免它,除非他们是单行)或(在一个单独的文件.cpp)头之外定义的所有方法。

UPDATE:

模板专业化的问题是,你最终定义现在的“正常”的方法:没有什么模板任何关于它的长,一旦你固定的每一个参数。因此,解决方案就像您对普通函数所做的那样:在头文件中声明并在源文件中定义(因此只有一次)。

要更具体地了解这个奇怪的错误:C++的问题在于每个源文件都是单独编译的:预处理器将采用include并实际创建一个包含每个包含文件的文本文件(按顺序),然​​后结束你的源代码。编译器接收这个文件并生成一个“.o”文件(用于gcc)。然后链接程序开始尝试实际创建一个库(或二进制文件)“。o“文件,并检查每种方法只定义一次,否则它将如何选择多个定义(不幸的是不检查它们是否等同或不...)。模板方法和类,并且它在所有实例化中随机选取一个(每个模板参数组合的一个实例化)。当然,这里假设它们都是相同的,你可能会得到相当的头痛类似:

// foo.h 
template <class T> int foo(T) { return 10; } 

// foo.cpp 
#include "foo.h" 

char a; 
std::cout << foo(a) << std::endl; 

// bar.cpp 
#include "foo.h" 

template <> int foo<char>(char) { return 20; } 

char b; 
std::cout << foo(b) << std::endl; 

这两条线将打印相同的输出,无论是10或20是未知的,虽然,可能会改变之间的建立!

+0

感谢您的详细发布以及内联建议,但正如我在更新中所述,我在明确的专业化方面存在问题,有什么建议? – Jacob 2010-01-22 19:42:36

4

MyLib命名空间是显而易见的方式 - 毕竟,这基本上是如何标准库,它可能比你的大得多。使用模板获得大量链接器错误是非常不寻常的,除非你有很多前向声明 - 你通常应该尽量避免。

+0

'std'肯定更大:) - 谢谢,我仔细看看命名空间的方式! – Jacob 2010-01-22 13:56:44

+0

我没有任何前向声明 - 我已经添加了对我已经获得的链接器错误的描述。 – Jacob 2010-01-22 16:20:09

3

使用命名空间是正确的方法。就个人而言,我不会将它们合并为一个“包含全世界”标题,因为这样会增加编译时间。其他人可能更喜欢单个标题的便利性。

如果有任何非模板函数,那么它们必须声明为inline,或者只在一个源文件中实现。模板函数和类定义中实现的类成员函数隐含地为inline,但其他函数不是。

+0

谢谢你的**内联**主意!我仍然有明确的专业化问题......任何想法? – Jacob 2010-01-22 19:41:54