让我开始使用dictionary.com 中的定义来回答
定义
元 -
添加到主题名称的前缀并指定另一个主题,该主题分析原始主题,但在更抽象,更高的层次上:元哲学;元语言学。
加在某事物名称上的前缀,有意识地引用或涉及其自身的主题或特征:
艺术家在画布。
模板编程最常被用作在 C++ 类型系统中表达关系的一种方式。因此,我认为可以公平地说模板编程天生就利用了类型系统本身。
从这个角度来看,我们可以直接应用上面给出的定义。模板编程和元(模板)编程的区别在于模板参数的处理和预期的结果。
检查其参数的模板代码显然属于前一种定义,而从模板参数创建新类型则属于后者。请注意,这还必须与您的代码对类型进行操作的意图相结合。
示例
让我们看一些例子:
std::aligned_storage的实现;
template<std::size_t Len, std::size_t Align /* default alignment not implemented */>
struct aligned_storage {
typedef struct {
alignas(Align) unsigned char data[Len];
} type;
};
此代码满足第二个条件,类型std::aligned_storage 用于创建另一个类型。我们可以通过创建包装器来使这一点更加清晰
template<typename T>
using storage_of = std::aligned_storage<sizeof(T), alignof(T)>::type;
现在我们实现了上述两个,我们检查参数类型 T,提取它的大小和对齐,然后我们使用该信息来构造一个依赖于我们的参数的新类型。这显然构成了元编程。
原来的std::aligned_storage 不太清楚,但仍然很普遍。我们以类型的形式提供结果,两个参数都用于创建新类型。可以说,在创建 type::data 的内部数组类型时会发生检查。
论证完整性的反例:
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue { /*Implementation defined implementation*/ };
说到这里,你可能会有疑问:
但是优先队列不也做类型检查,例如检索底层容器,或评估其迭代器的类型吗?
确实如此,但目标不同。 std::priority_queue 类型本身并不构成元模板编程,因为它没有利用信息在类型系统中进行操作。同时以下将是元模板编程:
template<typename C>
using PriorityQueue = std::priority_queue<C>;
这里的目的是提供一种类型,而不是对数据本身的操作。当我们查看可以对每个代码进行的更改时,这一点会变得更加清晰。
我们可以改变std::priority_queue 的实现,也许可以改变允许的操作。例如,为了支持更快的访问、额外的操作或容器内的位的紧凑存储。但所有这些完全是为了实际的运行时功能,与类型系统无关。
相比之下,看看我们可以对 PriotityQueue 做些什么。如果我们要选择不同的底层实现,例如,如果我们发现我们更喜欢 Boost.Heap 或者我们无论如何都链接到 Qt 并想要选择它们的实现,那么这就是单行更改。这就是元编程的目的,我们在其他类型形成的基于类型系统的参数中做出选择。
(元-)模板签名
关于你的测试,正如我们在上面看到的,storage_of 有专门的类型名参数,但很明显是元编程。如果你深入挖掘,你会发现类型系统本身,带有模板,是图灵完备的。甚至不需要明确说明任何整数变量,例如,我们可以通过递归堆叠模板轻松替换它们(即自然数的 Zermelo 构造)
using Z = void;
template<typename> struct Zermelo;
template<typename N> using Successor = Zermelo<N>;
在我看来,更好的测试是询问给定的实现是否具有运行时效果。如果模板结构或别名不包含任何仅在运行时产生效果的定义,则可能是模板元编程。
结束语
当然,普通的模板编程可能会使用元模板编程。您可以使用元模板编程来确定普通模板参数的属性。
例如,您可能会选择不同的输出策略(假设 template<class Iterator> struct is_pointer_like; 的某些元编程实现
template<class It> generateSomeData(It outputIterator) {
if constexpr(is_pointer_like<outputIterator>::value) {
generateFastIntoBuffer(static_cast<typename It::pointer> (std::addressof(*outputIterator));
} else {
generateOneByOne(outputIterator);
}
}
这构成了使用元模板编程实现的功能的模板编程。