我正在开发一个库,其中我们的许多核心对象都是模板,其中一个特定的实例显示在项目中的大多数文件中,其形式为指向该模板实例化的智能指针。我在单个源文件中显式实例化这些模板。我们最近切换到C ++ 11,我试图使用新的
extern template class MyTemplate<double>;
加快编译速度。 我的第一个问题是我是否使用智能指针
MyTemplate<double>
隐式实例化模板并需要&#34; extern模板。&#34;在文件的顶部,以避免重复实例化。
我的第二个问题是,是否有一些替代方法可以添加所有这些
extern template class MyTemplate<double>;
到每个源文件。对于我定义的每个模板,只需稍微繁琐地点击智能指针的每个实例,并确保我拥有正确的&#34; extern模板&#34;该文件中的行。我还可以看到为代码的未来开发人员执行此约定有点困难,因为他们可能会添加模板实例化并忘记相应的extern模板行,尤其是因为不会生成错误。
答案 0 :(得分:4)
如果你肯定知道你将明确地实例化模板,只需将显式实例化声明(extern template
行)放入标题中,以便它包含在模板中。
实例化文件中存在此行没有问题 - 标准明确允许它,只要显式实例化定义(非extern
)在后出现显式实例化声明(extern
一个)。该裁决采用C ++ 14 14.7.2 / 11;同样的裁决也出现在C ++ 11中。
答案 1 :(得分:4)
您可以将extern template
声明放入定义template
的头文件中。例如:
在档案useful.hxx
中:
#ifndef USEFUL_HXX
#define USEFUL_HXX
namespace my
{
template <typename T>
T
do_useful_stuff(T x)
{
return x;
}
extern template int do_useful_stuff(int);
extern template float do_useful_stuff(float);
// Potentially many more...
} // namespace my
#endif // ifndef USEFUL_HXX
在档案useful.cxx
中:
#include "useful.hxx"
namespace my
{
template int do_useful_stuff(int);
template float do_useful_stuff(float);
// Potentially many more...
} // namspace my
最后在档案main.cxx
中:
#include <iostream>
#include "useful.hxx"
int
main()
{
std::cout << my::do_useful_stuff(42) << std::endl;
std::cout << my::do_useful_stuff(1.0f) << std::endl;
}
您现在可以编译useful.cxx
和main.cxx
,然后将它们链接在一起。
到目前为止一切顺利。但是,正如您所看到的,这仍然是非常重复的。如果您不担心某些预处理器魔法,可以将常见内容分解为文件useful.txx
#ifndef MY_EXTERN
#error "Please '#define MY_EXTERN' to 'extern' or '' before '#include'ing this file."
#endif
namespace my
{
MY_EXTERN template int do_useful_stuff(int);
MY_EXTERN template float do_useful_stuff(float);
// Potentially many more...
} // namspace my
然后在#include
useful.cxx
#include "useful.hxx"
#define MY_EXTERN /* empty*/
#include "useful.txx"
#undef MY_EXTERN
和useful.hxx
#ifndef USEFUL_HXX
#define USEFUL_HXX
#ifndef MY_USE_EXTERN_TEMPLATES
#define MY_USE_EXTERN_TEMPLATES 0
#endif
namespace my
{
template <typename T>
T
do_useful_stuff(T x)
{
return x;
}
} // namespace my
#if MY_USE_EXTERN_TEMPLATES
#define MY_EXTERN extern
#include "useful.txx"
#undef MY_EXTERN
#endif
#endif // ifndef USEFUL_HXX
如图所示。请注意,我还利用这个机会使extern
声明成为条件,因此客户端可以将头文件与实例化模型一起使用。因此,我们也会更新main.cxx
:
#define MY_USE_EXTERN_TEMPLATES 1
#include <iostream>
#include "useful.hxx"
int
main()
{
std::cout << my::do_useful_stuff(42) << std::endl;
std::cout << my::do_useful_stuff(1.0f) << std::endl;
}
同样,我们可以编译useful.cxx
和main.cxx
并将生成的目标文件链接在一起。如果我们在#define MY_USE_EXTERN_TEMPLATES
中没有main.cxx
到1,则可以在一次刷新中编译和链接该文件。
答案 2 :(得分:0)
我还可以看到为我们代码的未来开发人员执行此约定有点困难,因为他们可能会添加模板实例化并忘记相应的extern模板行,尤其是因为不会生成错误。
我没有看到如何检测丢失的extern条目,但您可以使用静态断言,这将允许仅为列出的类型实例化模板。这些将是您明确实例化的类型。以下是如何检查类模板的T
是否来自允许类型列表(How to make a variadic is_same?)的示例:
// Template header
#include <string>
#include <vector>
template<typename T, typename... Rest>
struct is_any : std::false_type {};
template<typename T, typename First>
struct is_any<T, First> : std::is_same<T, First> {};
template<typename T, typename First, typename... Rest>
struct is_any<T, First, Rest...>
: std::integral_constant<bool, std::is_same<T, First>::value || is_any<T, Rest...>::value>
{};
template<typename T>
class MyT {
public:
MyT(); // must not be inline, otherwise no extern magic happens
};
extern template class MyT<double>;
extern template class MyT<std::string>;
// Somwhere in the app code
int main()
{
//MyT<int> dd1; // error
MyT<std::string> dd2; // ok
MyT<double> dd3; // ok
}
// Template implementation file
template<typename T>
MyT<T>::MyT() {
static_assert(is_any<T, double, std::string>::value, "ups... you forgot to make it extern template!");
}
template class MyT<double>;
template class MyT<std::string>;