题
我有一些模板的代码,我喜欢将储存在一个CPP文件,而不是在线的标题。我知道这是可以做到的只要你知道哪个模板类型将使用。例如:
.h文件
class foo
{
public:
template <typename T>
void do(const T& t);
};
.cpp文件
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::做模板功能仅用于整数和性传播疾病::串,因此这些定义意味着应用程序将链接。
我的问题-这是一个讨厌的黑客或者将这项工作与其他汇编者/接头?我只使用这个代码VS2008的时刻,但将想要口到其他环境。
解决方案
其他提示
对于本页面上的其他人,想知道显式模板专业化(或者至少在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>;
此代码格式正确。您只需要注意模板的定义在实例化时是可见的。引用标准,<!>#167; 14.7.2.4:
非导出的函数模板,非导出的成员函数模板或类模板的非导出成员函数或静态数据成员的定义应存在于显式实例化的每个转换单元中。
这应该适用于支持模板的所有地方。显式模板实例化是C ++标准的一部分。
你的例子是正确的,但不是很便携。还可以使用稍微清晰的语法(如@ namespace-sid所指出的那样)。
假设模板化的类是要共享的某个库的一部分。是否应编译其他版本的模板化类?图书馆维护者是否应该预测该课程的所有可能的模板用途?
另一种方法是对您所拥有的内容略有不同:添加第三个文件,即模板实现/实例化文件。
foo.h文件
// Standard header file guards omitted
template <typename T>
class foo
{
public:
void bar(const T& t);
};
foo.cpp文件
// Always include your headers
#include "foo.h"
template <typename T>
void foo::bar(const T& t)
{
// Do something with t
}
foo-impl.cpp文件
// Yes, we include the .cpp file
#include "foo.cpp"
template class foo<int>;
需要注意的是,您需要告诉编译器编译foo-impl.cpp
而不是foo.cpp
,因为编译后者什么都不做。
当然,您可以在第三个文件中有多个实现,或者为您要使用的每种类型提供多个实现文件。
当共享模板化类用于其他用途时,这可以提供更大的灵活性。
此设置还减少了重用类的编译时间,因为您没有在每个翻译单元中重新编译相同的头文件。
这绝对不是一个讨厌的黑客,但要注意你必须为你想要与给定模板一起使用的每个类/类型(显式模板特化)这样做。如果许多类型请求模板实例化,则.cpp文件中可能有很多行。要解决此问题,您可以在每个使用的项目中使用TemplateClassInst.cpp,以便更好地控制将实例化的类型。显然这个解决方案并不完美(也就是银弹),因为你可能最终打破了ODR :)。
在最新的标准中,有一个关键字(export
)可以帮助缓解这个问题,但除了Comeau之外,它并没有在我所知道的任何编译器中实现。
请参阅 FAQ-lite
是的,这是进行 specializiation 显式实例化的标准方法。如您所述,您无法使用其他类型实例化此模板。
编辑:根据评论更正。
这是定义模板功能的标准方法。我认为我有三种方法可以用来定义模板。或者可能4.各有利弊。
-
在类定义中定义。我根本不喜欢这个,因为我认为类定义仅供参考,应该易于阅读。然而,在课堂上定义模板比在外面定义模板要困难得多。并非所有模板声明都具有相同的复杂程度。此方法还使模板成为真正的模板。
-
在同一标题中定义模板,但在类之外。这是我大多数时候的首选方式。它使您的类定义保持整洁,模板仍然是一个真正的模板。然而,它需要完整的模板命名,这可能很棘手。此外,您的代码可供所有人使用。但是如果你需要你的代码是内联的,这是唯一的方法。您还可以通过在类定义的末尾创建.INL文件来实现此目的。
-
将header.h和implementation.CPP包含在main.CPP中。我认为这就是它的完成方式。您不必准备任何预实例,它将表现得像一个真正的模板。我遇到的问题是它不自然。我们通常不包括并期望包含源文件。我猜你自己包含了源文件,可以内联模板函数。
-
最后一种方法是发布方式,它定义了源文件中的模板,就像数字3一样;但是,不是包含源文件,我们预先将模板实例化为我们需要的模板。我对这种方法没有任何问题,它有时会派上用场。我们有一个很大的代码,它不能从内联中受益,所以只需将其放入CPP文件即可。如果我们知道常见的实例,我们可以预定义它们。这节省了我们基本相同的东西5,10次。这种方法的好处是保持我们的代码专有。但我不建议在CPP文件中放置微小的,经常使用的功能。因为这会降低库的性能。
醇>
注意,我不知道膨胀的obj文件的后果。
你给出的例子没有错。但我必须说我相信将函数定义存储在cpp文件中效率不高。我只理解需要将函数的声明和定义分开。
当与显式类实例化一起使用时,Boost概念检查库(BCCL)可以帮助您在cpp文件中生成模板功能代码。
更新时间!创建内联(.inl或可能是任何其他)文件,只需复制其中的所有定义。请务必在每个函数(template <typename T, ...>
)上方添加模板。现在,不是将头文件包含在内联文件中,而是执行相反的操作。在您的类(#include "file.inl"
)的声明之后包括内联文件。
我真的不知道为什么没人提到这一点。我认为没有直接的弊端。
我们举一个例子,假设您想要一个模板类:
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
template <>
void DemoT<int>::test()
{
printf("int test (int)\n");
}
template <>
void DemoT<bool>::test()
{
printf("int test (bool)\n");
}
如果使用Visual Studio编译此代码 - 它开箱即用。 gcc将产生链接器错误(如果从多个.cpp文件中使用相同的头文件):
error : multiple definition of `DemoT<int>::test()'; your.o: .../test_template.h:16: first defined here
可以将实现移动到.cpp文件,但是你需要像这样声明类 -
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
template <>
void DemoT<int>::test();
template <>
void DemoT<bool>::test();
// Instantiate parametrized template classes, implementation resides on .cpp side.
template class DemoT<bool>;
template class DemoT<int>;
然后.cpp看起来像这样:
//test_template.cpp:
#include "test_template.h"
template <>
void DemoT<int>::test()
{
printf("int test (int)\n");
}
template <>
void DemoT<bool>::test()
{
printf("int test (bool)\n");
}
在头文件中没有最后两行 - gcc可以正常工作,但Visual Studio会产生错误:
error LNK2019: unresolved external symbol "public: void __cdecl DemoT<int>::test(void)" (?test@?$DemoT@H@@QEAAXXZ) referenced in function
如果你想通过.dll导出公开函数,模板类语法是可选的,但这仅适用于windows平台 - 所以test_template.h可能如下所示:
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif
template <>
void DLL_EXPORT DemoT<int>::test();
template <>
void DLL_EXPORT DemoT<bool>::test();
使用上一个示例中的.cpp文件。
但这会给链接器带来更多麻烦,因此如果不导出.dll函数,建议使用前面的示例。