创建模板函数库

时间:2010-01-22 13:42:41

标签: c++ templates

我一直在开发一个主要是模板函数的库,并设法通过以下方式保持组织(在某种程度上):

// 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:

通过在单独的文件中定义模板函数解决了显式特化问题 - 感谢您的帮助!

3 个答案:

答案 0 :(得分: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;
}

一个激励的例子? Boost做到了(带有商品标题)。

而且这不会花费太多:只需将class替换为namespace并删除static个关键字,这就是所有人。

对于链接器问题:所有未模板化的方法都应声明为inline(除非它们是单行,否则尝试避免它)或在标题之外定义(在单独的{{ 1}}文件)。

<强>更新

模板特化的问题在于你最终定义了一个现在的“普通”方法:一旦你修复了每个参数,就不再有关于它的模板了。因此,解决方案就像您对正常函数所做的那样:在头文件中声明和在源文件中定义(因此只有一次)。

更具体地说明这个奇怪的错误:C ++的问题是每个源文件都是独立编译的:预处理器将采用include并实际创建一个包含每个包含文件的文本文件(在订单)然后你的来源在最后。编译器获取此文件并生成“.o”文件(对于gcc)。然后链接器启动并尝试从所有这些“.o”文件中实际创建库(或二进制文件),并检查每个方法是否只定义一次,否则它将如何在多个定义之间进行选择(遗憾的是不会检查它们是否相同......)?

虽然模板方法和类有一个特殊的限制,它在所有实例化中(每个模板参数组合的一个实例)中选取一个(随机)。当然,这假设所有这些都是相同的,你最终会因为类似的事情而头疼:

.cpp

两条线都会打印相同的输出,不管它是10还是20都是未知的,并且可以在构建之间改变!!!

答案 1 :(得分:4)

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

答案 2 :(得分:3)

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

如果存在任何非模板函数,则必须将它们声明为inline,或者仅在一个源文件中实现。模板函数和类定义中实现的类成员函数隐式inline,但其他函数不是。{/ p>