替代" extern模板"在每个源文件中

时间:2015-04-23 16:06:40

标签: c++ templates c++11

我正在开发一个库,其中我们的许多核心对象都是模板,其中一个特定的实例显示在项目中的大多数文件中,其形式为指向该模板实例化的智能指针。我在单个源文件中显式实例化这些模板。我们最近切换到C ++ 11,我试图使用新的

extern template class MyTemplate<double>;

加快编译速度。 我的第一个问题是我是否使用智能指针

MyTemplate<double>

隐式实例化模板并需要&#34; extern模板。&#34;在文件的顶部,以避免重复实例化。

我的第二个问题是,是否有一些替代方法可以添加所有这些

extern template class MyTemplate<double>;

到每个源文件。对于我定义的每个模板,只需稍微繁琐地点击智能指针的每个实例,并确保我拥有正确的&#34; extern模板&#34;该文件中的行。我还可以看到为代码的未来开发人员执行此约定有点困难,因为他们可能会添加模板实例化并忘记相应的extern模板行,尤其是因为不会生成错误。

3 个答案:

答案 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.cxxmain.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.cxxmain.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>;