我想了解什么是元编程以及它在一般情况下的含义,以及在C++中的具体表现。如果我搜索c++元编程,我会得到关于模板元编程(TMP)的教程,但没有解释是否它只对特定用途的模板进行分类还是所有用途的模板都包括在内。
我的问题是是否将C++中所有用途的模板都归类为元编程。对于为什么是或者不是的解释也会有所帮助。谢谢。
我想了解什么是元编程以及它在一般情况下的含义,以及在C++中的具体表现。如果我搜索c++元编程,我会得到关于模板元编程(TMP)的教程,但没有解释是否它只对特定用途的模板进行分类还是所有用途的模板都包括在内。
我的问题是是否将C++中所有用途的模板都归类为元编程。对于为什么是或者不是的解释也会有所帮助。谢谢。
template <typename K, typename V>
void printKeyVal (K const & k, V const & v)
{ std::cout << k << ": " << v << std::endl; }
printKeyVal()
是一个模板函数,其作用是将一些通用值打印到标准输出(因此是运行时,而不是编译时)。
由于它不能在编译时运行,因此被称为“模板”,但并不是“元编程”。
更一般地说,std::vector
是一个使用内存分配的模板类。而内存分配(直到C++17;可能在未来会有所改变)不能用于编译时代码。
因此,std::vector
(与具有固定大小且不使用内存分配的std::array
相反)是一个模板特性,当使用实例化了std::vector
对象时无法用于元编程。
printKeyVal
根据模板参数在编译时定义。 - SomeWittyUsernameC++中的模板元编程(TMP)是一种使用C++模板在编译时表达和执行任意算法的技术。它通常通过使用模板特化来模拟条件分支和递归模板定义来模拟循环来实现。最著名的例子是编译时阶乘计算:
template <unsigned int n>
struct factorial {
// recursive definition to emulate a loop or a regular recursion
enum { value = n * factorial<n - 1>::value };
};
// specialization that describes "break" condition for the recursion
template <>
struct factorial<0> {
enum { value = 1 };
};
这个技术同时使用了上述两种技术。
然而,更为常见的是使用TMP进行类型检测和转换,而不是进行实际的数值计算,例如标准的std::is_pointer
工具(source):
// generic definition that emulates "false" conditional branch
template<class T>
struct is_pointer_helper : std::false_type {};
// a specialization that emulates "true" conditional branch
template<class T>
struct is_pointer_helper<T*> : std::true_type {};
template<class T>
struct is_pointer : is_pointer_helper< typename std::remove_cv<T>::type > {};
标准type_traits头文件提供的大多数实用程序都是使用TMP技术实现的。
鉴于TMP算法是使用类型定义表示的,值得一提的是,TMP是一种声明式编程形式,其中计算逻辑是不使用显式控制流语句(if
,else
,for
等)来表达的。
简短的回答是:不是。如果模板未用于表达编译时算法,则它不是元编程,而是泛型编程。
在C++中引入模板的主要目的是实现泛型编程,即允许重用相同的算法(如find
、copy
、sort
等)和数据结构(如vector
、list
、map
等)对于任何类型,包括用户定义的类型,只要这些类型满足某些要求。#define MAX(a,b) ((a) > (b) ? (a) : (b))
max
函数的写法,没有使用模板。我已经说过,我不认为预处理器是元编程,但无论如何,每次使用它时它总是生成相同的代码。a>b
到编译器。这里没有任何东西在编译时运行以根据...任何内容产生不同的结果代码。在编译时,没有计算任何内容。template <typename T>
T max(T a, T b) { return a > b ? a : b; }
这并不仅仅是一个文本替换操作。实例化的过程更加复杂,需要考虑名称查找规则和函数重载问题,并且从某种意义上讲,不同的实例化可能在文本上并不等价(例如,一个实例化可能使用bool ::operator<(T,T)
,而另一个实例化可能使用bool T::operator<(T const&)
或其他方式)。
然而,每个实例化的含义相同(假设不同类型之间定义了兼容的operator<
),除编译器通常处理类型、名称解析等机械过程外,在编译时没有计算任何内容。
顺便说一下,你的程序包含指令,告诉编译器该执行什么操作是绝对不够的,因为所有的编程都是如此。
现在,还有一些边角案例:
template <unsigned N>
struct factorial() { enum { value = N * factorial<N-1>::value }; };
这些操作确实将计算移动到编译时(在这种情况下是一个非终止计算,因为我无法编写终止情况),但可以说它们不是元编程。
尽管维基百科的定义提到了将计算移动到编译时,但这只是一个值计算 - 它没有对代码的结构或语义做出编译时决策。
std::vector
时,我指示编译器生成一个程序(因此它确实是元编程),但是这个指令本身几乎不是一个程序。大多数人不认为使用宏是“元编程”。 - n. m.