【问题标题】:Can I declare a template specialization from a typelist?我可以从类型列表中声明模板特化吗?
【发布时间】:2015-09-07 18:23:53
【问题描述】:

很确定我已经知道这个问题的答案,但值得一试。

所以,假设我有一个类型列表:

template <typename ...Ts>
struct typelist{};

其中包含一些对象:

struct foo{};
struct bar{};
struct quux{};

using objects = typelist<foo, bar, quux>;

现在我有一个模板类 (baz) 可以接受任何这些对象。但是,由于代码库大小和编译时间的原因,我希望在 cpp 文件中实现我的模板化方法。

所以在 baz.cpp 的底部我有:

template <> class baz<foo>;
template <> class baz<bar>;
template <> class baz<quux>;

问题是我有很多像baz 这样的类,它们使用的对象列表也在不断变化。那么......无论如何我可以保留我的单一对象类型列表并在每个baz-like 对象的 cpp 文件中使用它来进行专业化吗?然后,我所要做的就是在我有一个新对象时更新我的​​类型列表,并且所有对象文件都将重建。

【问题讨论】:

  • 您不必专门化,只需明确实例化它们。
  • 这绝对可以使用宏来完成。鉴于这将与您的其他代码库隔离,为什么不这样做呢?

标签: c++ templates metaprogramming template-specialization


【解决方案1】:

template &lt;&gt; class baz&lt;foo&gt;; 行向前声明了一个特化而不是模板实例化,我认为这是您想要的。

我认为没有直接的方法可以做到这一点,您必须进行一些元编程。您可以使用Boost.Preprocessor 生成所有需要的代码:

#define TYPES (foo)(bar)(quux)

using objects = typelist< BOOST_PP_SEQ_ENUM(TYPES) >;

// Generate extern template declarations in the header
#define EXTERN_TEMPLATE_BAZ(r, data, arg)\
    extern template class baz< arg >;

BOOST_PP_SEQ_FOR_EACH(EXTERN_TEMPLATE_BAZ, _, TYPES)

// Generate template instantiations in the .cpp
#define TEMPLATE_BAZ(r, data, arg)\
    template class baz< arg >;

BOOST_PP_SEQ_FOR_EACH(TEMPLATE_BAZ, _, TYPES)

可能有一种方法可以在没有预处理器的情况下执行此操作,但这样做会对baz 类型提出额外的要求。关键是在必须实例化它的上下文中使用该类型,包括它的所有方法。

【讨论】:

  • 不是每个人都使用 boost 并且问题中没有关于 boost 的注释。
  • 所以?尽管如此,这是一个有效的解决方案。
  • 此解决方案有效,但可能不是提问者想要的。 Boost 不是标准 C++ 的一部分。
  • 我只是提出了我认为最有效的解决方案——我已经多次以这种方式解决了类似的任务。如果 Boost 不可用,则会给出多个其他答案。我让提问者决定什么最适合他。
  • 提升完全没问题!
【解决方案2】:

如果不使用预处理器,我很确定这是不可能的。您可能能够从参数重建模板参数包,但您必须实际传递参数的一个实例,这似乎不是最佳的。其次,在块范围内(即在模板函数中)不允许显式模板实例化,因此无法编写显式实例化另一个模板的模板。

正如 Nir ​​所说,你为什么不直接使用X Macro

#define MY_FOREACH_TYPES(func, ...) \
  func(type1, ##_VA_ARGS__) \
  func(type2, ##_VA_ARGS__) \

#define MY_INSTANTIATE(Type, Class) \
  template <> class Class<Type>;

MY_FOREACH_TYPES(MY_INSTANTIATE, bar)

现在只需在您的类型列表更改时更新 MY_FOREACH_TYPES。

【讨论】:

  • 非常感谢您提及 X-Macro。维基链接是最好的!你知道是什么让人们不使用 X Macro 吗? (除了更难跟踪/调试)
【解决方案3】:

第一件事:显式类模板实例化的正确语法是

template class baz<foo>;
template class baz<bar>;
template class baz<quux>;

不是template &lt;&gt; class baz&lt;foo&gt;,它是明确的类模板特化(前向声明)。

一种可能性是实例化一个看起来像这样的类

template <template <typename> class T, typename... Args>
class for_each_class : T<Args>...
{
};

// Instantiate
template class for_each_class<baz, foo, bar, quux>;

这将强制 baz&lt;foo&gt;baz&lt;bar&gt;baz&lt;quux&gt; 的隐式实例化。好吧,但是您想从typelist 创建它。 typelist 是一个已经专门化的模板,在 C++ 中无法从“typelist 的外部世界”通过 typelist 中的模板参数进行迭代。

另一种可能是使用宏,但即使在宏中,您也不能使用原来的typelist。我会得出结论,你的问题没有解决给定的typelist

作为一种解决方案,如果可能,我会将模板实例化留在编译器上。在这种情况下,未使用的模板不会被实例化。编译慢的原因是meta-programs are specified的方式。

【讨论】:

  • 不会实例化 baz&lt;foo&gt;baz&lt;bar&gt;baz&lt;quux&gt;
  • 因为模板成员没有被实例化。
  • @Barry 请再次阅读我的帖子。我不是说这会实例化baz&lt;foo&gt; 等,但我在解释为​​什么没有宏就不能这样做。
  • 您的帖子说“一种可能性是实例化一个看起来像...的类,这将强制隐式实例化 baz&lt;foo&gt;, ...”
【解决方案4】:

使用普通预处理器的版本

//Header file

#define BAZ_OBJS \
    BAZ_BEGIN   foo \
    BAZ_AND     bar \
    BAZ_AND     quux \
    BAZ_END

#define BAZ_BEGIN
#define BAZ_AND ,
#define BAZ_END 
using objects = typelist<BAZ_OBJS>;
#undef BAZ_BEGIN
#undef BAZ_AND
#undef BAZ_END

#define BAZ_BEGIN BAZ_EXTERN template class baz<
#define BAZ_END >;
#define BAZ_AND BAZ_END BAZ_BEGIN

#ifdef MY_IMPLEMENTATION_CPP  //cpp should define it before including the header file
#define BAZ_EXTERN
#else
#define BAZ_EXTERN extern
#endif

BAZ_OBJS

【讨论】:

    【解决方案5】:

    这样就可以了。最终,只有一种(或没有)类型的类型列表专业化。

    template <typename Head, typename ...Tail>
    struct typelist{
        typedef baz<Head> head_t;
        typedef typelist<Tail...> tail_t;
    };
    

    【讨论】:

    • 不,它不会实例化baz
    • 如果 baz&lt;Head&gt;typelist&lt;Head, /*...*/&gt; 的属性怎么办?我目前无法对其进行测试;我得到的最新编译器是 gcc4.3。
    • 什么属性?我不知道执行模板实例化的任何属性。
    猜你喜欢
    • 2015-05-26
    • 2021-12-18
    • 2019-01-01
    • 2023-04-08
    • 2011-01-30
    • 1970-01-01
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多