代码之家  ›  专栏  ›  技术社区  ›  Jacob

创建模板函数库

  •  2
  • Jacob  · 技术社区  · 14 年前

    我一直在开发一个主要包含模板功能的库,并设法以以下方式(在某种程度上)组织事情:

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

    很明显,电话是这样打的:

    MyLib::Func1();
    

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

    我最初考虑在 MyLib 名称空间,然后使用 MyLib.h 为了合并所有这些错误,但是我不断收到大量的链接器错误——当然,如果建议使用这种方法的话,我可以仔细研究一下。

    有什么想法吗?

    PS: 因为这些函数中的大多数都有不同的目标,所以将它们分组到一个类下(我们从该类实例化对象)是没有意义的。我用过了 class 在这里,我不必担心我定义函数的顺序(函数内部存在相互依赖关系) 迈利布 同样如此。

    链接错误:

    所以基本结构是这样的:我有两个类(比如A&B)编译成静态库,还有一个主应用程序运行这些类的实例。这些类A&B在 迈利布 . 当A&B编译时,我得到 LNK4006 警告,说明符号属于 我的图书馆 已在中定义 OBJ 项目中的文件,它将忽略它。

    当涉及到应用程序时, LNK2005 声明它已在中定义的错误 OBJ A&B的文件。

    更新: 谢谢你 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:

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

    3 回复  |  直到 14 年前
        1
  •  5
  •   Matthieu M.    14 年前

    与使用静态方法的类相比,您应该更喜欢使用命名空间:

    • 名称空间为您提供了在多个文件之间共享的可能性,每个逻辑方法组一个。
    • 命名空间可以省略:可能是因为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 (尽量避免,除非它们是一个衬板)或在收割台外部定义(在单独的 .cpp 文件)。

    更新:

    模板专门化的问题在于,您最终定义了一个现在的“普通”方法:一旦修复了每个参数,它就不再有任何模板了。因此,解决方案是像处理普通函数那样:在头文件中声明,在源文件中定义(因此只有一次)。

    对这个奇怪的错误要更具体一些:C++的问题是,每个源文件都是孤立编译的:预处理器将包含并实际创建一个包含每一个包含的文件(按顺序)的单个文本文件,最后是源文件。编译器获取此文件并生成一个“.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,都是未知的,并且可以在构建之间更改!!!!

        2
  •  4
  •   anon    14 年前

    mylib名称空间是一种显而易见的方式——毕竟,这基本上就是标准库的方式,它可能比您的库大得多。在模板中出现大量的链接器错误是不常见的,除非您有大量的前向声明——通常应该尽量避免这种情况。

        3
  •  3
  •   Mike Seymour    14 年前

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

    如果有任何非模板函数,则必须声明它们 inline 或仅在一个源文件中实现。模板函数和类定义内实现的类成员函数是隐式的 内联的 ,但其他函数没有。