首页 文章

在.CPP文件中存储C模板函数定义

提问于
浏览
403

我有一些模板代码,我宁愿存储在CPP文件中而不是 Headers 中的内联 . 我知道只要您知道将使用哪些模板类型,就可以完成此操作 . 例如:

.h file

class foo
{
public:
    template <typename T>
    void do(const T& t);
};

.cpp file

template <typename T>
void foo::do(const T& t)
{
    // Do something with t
}

template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);

注意最后两行--foo :: do模板函数仅用于int和std :: strings,因此这些定义意味着应用程序将链接 .

我的问题是 - 这是一个讨厌的黑客还是会与其他编译器/链接器一起使用?我目前只在VS2008上使用此代码,但是想要移植到其他环境 .

11 回答

  • 3

    在最新的标准中,有一个关键字( export )可以帮助缓解这个问题,但除了Comeau之外,它还没有意识到 .

    请参阅FAQ-lite .

  • 8

    是的,这是进行特殊化显式实例化的标准方法 . 如您所述,您无法使用其他类型实例化此模板 .

    编辑:根据评论更正 .

  • 176

    你给出的例子没有错 . 但我必须说我相信将函数定义存储在cpp文件中效率不高 . 我只理解需要分离函数的声明和定义 .

    当与显式类实例化一起使用时,Boost概念检查库(BCCL)可以帮助您在cpp文件中生成模板功能代码 .

  • 11

    对于本页面上的其他人,想知道显式模板专业化(或至少在VS2008中)的正确语法(就像我一样),其以下内容......

    在你的.h文件中......

    template<typename T>
    class foo
    {
    public:
        void bar(const T &t);
    };
    

    在你的.cpp文件中

    template <class T>
    void foo<T>::bar(const T &t)
    { }
    
    // Explicit template instantiation
    template class foo<int>;
    
  • 17

    这是定义模板函数的标准方法 . 我认为我有三种方法可以用来定义模板 . 或者可能4.每个都有利有弊 .

    • 在类定义中定义 . 我根本不喜欢这个,因为我认为类定义仅供参考,应易于阅读 . 然而,在课堂上定义模板比在外面定义模板要简单得多 . 并非所有模板声明都处于相同的复杂程度 . 此方法还使模板成为真正的模板 .

    • 在同一 Headers 中定义模板,但在类之外 . 这是我大多数时候的首选方式 . 它使您的类定义保持整洁,模板仍然是一个真正的模板 . 然而,它需要完整的模板命名,这可能很棘手 . 此外,您的代码可供所有人使用 . 但是,如果您需要内联代码,这是唯一的方法 . 您还可以通过在类定义的末尾创建.INL文件来完成此操作 .

    • 将header.h和implementation.CPP包含到main.CPP中 . 我认为这就是它的完成方式 . 您不必准备任何预实例,它将表现得像一个真正的模板 . 我遇到的问题是它不自然 . 我们通常不包括并期望包含源文件 . 我猜你自己包含了源文件,可以内联模板函数 .

    • 最后一种方法是发布方式,它定义了源文件中的模板,就像数字3一样;但是,不是包含源文件,我们预先将模板实例化为我们需要的模板 . 我对这种方法没有任何问题,有时会派上用场 . 我们有一个很大的代码,它不能从内联中受益,所以只需将其放入CPP文件即可 . 如果我们知道常见的实例,我们可以预定义它们 . 这使我们免于书写基本相同的事情5,10次 . 这种方法的好处是保持我们的代码专有 . 但我不建议在CPP文件中放置微小的,经常使用的功能 . 因为这会降低库的性能 .

    注意,我不知道膨胀的obj文件的后果 .

  • -1

    更新时间!创建内联(.inl,或可能是任何其他)文件,只需复制其中的所有定义 . 请务必在每个函数( template <typename T, ...> )上方添加模板 . 现在,不是将头文件包含在内联文件中,而是执行相反的操作 . 包括内联文件 after 您的类的声明( #include "file.inl" ) .

    我真的不知道为什么没人提到这一点 . 我认为没有直接的缺点 .

  • 0

    这绝对不是一个讨厌的黑客,但要注意这个事实,你必须为你想要与给定模板一起使用的每个类/类型(明确的模板特化) . 如果MANY类型请求模板实例化,则.cpp文件中可能有很多行 . 要解决此问题,您可以在每个使用的项目中使用TemplateClassInst.cpp,以便更好地控制将实例化的类型 . 显然这个解决方案并不完美(也就是银弹),因为你最终可能会破坏ODR :) .

  • 4

    你的例子是正确的,但不是很便携 . 还可以使用稍微清晰的语法(如@ namespace-sid所指出的) .

    假设模板化类是要共享的某个库的一部分 . 是否应编译其他版本的模板化类?图书馆维护者是否应该预测课程的所有可能的模板用途?

    另一种方法是对您所拥有的内容略有不同:添加第三个文件,即模板实现/实例化文件 .

    foo.h file

    // Standard header file guards omitted
    
    template <typename T>
    class foo
    {
    public:
        void bar(const T& t);
    };
    

    foo.cpp file

    // Always include your headers
    #include "foo.h"
    
    template <typename T>
    void foo::bar(const T& t)
    {
        // Do something with t
    }
    

    foo-impl.cpp file

    // Yes, we include the .cpp file
    #include "foo.cpp"
    template class foo<int>;
    

    需要注意的是,您需要告诉编译器编译 foo-impl.cpp 而不是 foo.cpp ,因为编译后者什么都不做 .

    当然,您可以在第三个文件中有多个实现,或者为您要使用的每种类型提供多个实现文件 .

    当共享模板化类用于其他用途时,这可以提供更大的灵活性 .

    此设置还减少了重用类的编译时间,因为您不会重新编译相同的类每个翻译单元中的头文件 .

  • 5

    此代码格式正确 . 您只需要注意模板的定义在实例化时是可见的 . 引用标准,§14.7.2.4:

    非导出的函数模板,非导出的成员函数模板或类模板的非导出成员函数或静态数据成员的定义应存在于显式实例化的每个转换单元中 .

  • 0

    这应该适用于所有支持模板的地方 . 显式模板实例化是C标准的一部分 .

  • 90

    您描述的问题可以通过在 Headers 中定义模板或通过您在上面描述的方法来解决 .

    我建议从C++ FAQ Lite阅读以下几点:

    他们详细介绍了这些(和其他)模板问题 .

相关问题