我可以从类型列表声明模板实例化吗?

问题描述 投票:0回答:6

很确定我已经知道答案了,但值得一试。

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

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
的对象的 cpp 文件中使用它来进行专门化?然后,我所要做的就是当我有一个新对象时更新我的类型列表,并且所有对象文件都将重建。

c++ templates metaprogramming template-specialization
6个回答
11
投票

template <> class baz<foo>;
行向前声明专业化而不是模板实例化,我认为这就是您想要的。

我认为没有直接的方法可以做到这一点,您必须进行一些元编程。您可以使用 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
类型提出额外的要求。重点是在必须实例化该类型的上下文中使用该类型,包括其所有方法。


4
投票

首先要做的事情:显式类模板实例化的正确语法是

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

不是

template <> class baz<foo>
,这是显式类模板专业化(前向声明)。

一种可能是实例化一个如下所示的类

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

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

这将强制隐式实例化

baz<foo>
baz<bar>
baz<quux>
。好吧,但你想从
typelist
创建这个。
typelist
是一个已经专门化的模板,在 C++ 中无法通过“
typelist
的外部世界”中的
typelist
中的模板参数进行迭代。

另一种可能性是使用宏,但即使在宏中你也不能使用原来的

typelist
。我的结论是你的问题没有通过给定的
typelist
得到解决。

作为解决方案,如果可能的话,我会将模板实例化保留在编译器上。在这种情况下,未使用的模板不会被实例化。编译缓慢是由于指定元程序的方式


3
投票

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

正如 Nir 所言,为什么不直接使用 X 宏

#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 即可。


2
投票

与普通预处理器一起使用的版本

//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

1
投票

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

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

0
投票

如果您无法修改现有的类型列表(如https://stackoverflow.com/a/33084314/567292),但有理由确定它的长度不会改变,您仍然可以使用Boost.Preprocessor与 Boost.MP11:

#define OBJECTS_SIZE 3  // adjust as necessary
static_assert(boost::mp11::mp_size<objects>::value == OBJECTS_SIZE);
#define INSTANTIATE_BAZ(z, N, _) template class baz<boost::mp11::mp_at_c<objects, N>>;
BOOST_PP_REPEAT(OBJECTS_SIZE, INSTANTIATE_BAZ, _)

每当类型列表的长度发生变化时,就需要调整宏

OBJECTS_SIZE
,但希望这种情况不会太频繁,并且现代编译器应该能够告诉您应该将值更改为什么。您甚至可以在构建系统中生成它并在编译器命令行上传递它。

© www.soinside.com 2019 - 2024. All rights reserved.