n00*_*ki3 30 c++ templates metaprogramming
我有一个问题......我不懂模板元编程.
问题是:我读了很多.但这对我来说没有多大意义:/
事实nr.1:模板元编程更快
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
int x = Factorial<4>::value; // == 24
int y = Factorial<0>::value; // == 1
}
Run Code Online (Sandbox Code Playgroud)
所以这个Metaprogram更快......因为Constant Literal.
但是:在现实世界中我们有不变的文字吗?
我用的大多数程序都会对用户输入作出反
事实nr.2:模板元编程可以实现更好的可维护性.
是啊.析因示例可以维护......但是当涉及到复杂的函数时,我和大多数其他C++程序员都无法读取函数.
调试选项很差(或者至少我不知道如何调试).
模板元编程在哪里有意义?
Dan*_*ker 28
正如因子不是非函数语言中递归的现实例子,它也不是模板元编程的现实例子.这只是人们想要向您展示递归的标准示例.
在为实际目的编写模板时,例如在日常库中,模板通常必须根据实例化的类型参数来调整它所做的事情.这可能变得非常复杂,因为模板有条件地有效地选择要生成的代码.这就是模板元编程的原因; 如果模板必须循环(通过递归)并在备选之间进行选择,它实际上就像在编译期间执行以生成正确代码的小程序.
这是一个来自boost文档页面的非常好的教程(实际上摘自一本精彩的书,非常值得一读).
http://www.boost.org/doc/libs/1_39_0/libs/mpl/doc/tutorial/representing-dimensions.html
Lir*_*una 15
我使用模板mete-programming为SSE调配操作符在编译期间优化shuffle.
SSE swizzles('shuffles')只能被屏蔽为字节文字(立即值),因此我们创建了一个'mask merger'模板类,它在编译时合并掩码,以便在发生多次shuffle时:
template <unsigned target, unsigned mask>
struct _mask_merger
{
enum
{
ROW0 = ((target >> (((mask >> 0) & 3) << 1)) & 3) << 0,
ROW1 = ((target >> (((mask >> 2) & 3) << 1)) & 3) << 2,
ROW2 = ((target >> (((mask >> 4) & 3) << 1)) & 3) << 4,
ROW3 = ((target >> (((mask >> 6) & 3) << 1)) & 3) << 6,
MASK = ROW0 | ROW1 | ROW2 | ROW3,
};
};
Run Code Online (Sandbox Code Playgroud)
这可以工作并产生非凡的代码,而不会产生代码开销和额外的编译时间.
所以这个Metaprogram更快......因为Constant Literal.但是:在现实世界中我们有不变的文字吗?我使用的大多数程序都会对用户输入作出反应
这就是为什么它几乎没有用于价值观.通常,它用于类型.使用类型来计算和生成新类型.
有许多现实世界的用途,即使你没有意识到,其中一些你已经熟悉了.
我最喜欢的一个例子是迭代器.它们主要是用通用编程设计的,是的,但模板元编程特别适用于某个地方:
修补指针,以便它们可以用作迭代器.迭代器必须暴露一些typedef,例如value_type.指针不这样做.
所以代码如下(基本上与你在Boost.Iterator中找到的相同)
template <typename T>
struct value_type {
typedef typename T::value_type type;
};
template <typename T>
struct value_type<T*> {
typedef T type;
};
Run Code Online (Sandbox Code Playgroud)
是一个非常简单的模板元程序,但它非常有用.它允许您获取任何迭代器类型T的值类型,无论它是指针还是类,只需通过value_type<T>::type.
我认为上述在可维护性方面有一些非常明显的好处.只在迭代器上运行的算法必须实现一次.如果没有这个技巧,你必须为指针做一个实现,而另一个用于"适当的"基于类的迭代器.
类似的技巧boost::enable_if也非常有价值.您有一个函数的重载,应该只为特定的类型集启用.您可以使用元编程来指定条件并将其传递给每个类型,而不是为每种类型定义重载enable_if.
Earwicker已经提到了另一个很好的例子,一个表达物理单位和维度的框架.它允许您表达附加物理单位的计算,并强制执行结果类型.乘以米为单位可以产生数平方米.模板元编程可用于自动生成正确的类型.
但大多数情况下,模板元编程在小的,孤立的情况下使用(并且有用),基本上是为了平滑凸起和异常情况,使一组类型看起来和行为均匀,允许您更有效地使用通用编程
支持Alexandrescu的Modern C++ Design的建议.
当你正在编写一个可以在"选择Foo,Bar和Baz"方法中组合组合的库时,模板真的很闪耀,并且您希望用户以某种形式使用这些片段编译时间.例如,我合着了一个数据挖掘库,它使用模板元编程让程序员决定DecisionType使用什么(分类,排序或回归),InputType期望什么(浮点数,整数,枚举值,等等),以及KernelMethod使用什么(它是一个数据挖掘的事情).然后,我们为每个类别实现了几个不同的类,以便有几十种可能的组合.
实现60个单独的类来执行此操作将涉及许多烦人的,难以维护的代码重复.模板元编程意味着我们可以将每个概念作为代码单元实现,并为程序员提供一个简单的接口,用于在编译时实例化这些概念的组合.
维度分析也是一个很好的例子,但其他人已经涵盖了这一点.
我曾经写过一些简单的编译时伪随机数生成器,只是为了弄乱人们的脑袋,但这并不能算上IMO.
阶乘的例子对于真实世界的TMP来说就像"Hello,world!"一样有用.用于通用编程:它是为了向您展示一些有用的技术(递归而不是迭代,"else-if-then"等),这是一个非常简单,相对容易理解的示例,与您的每一个都没有多大关系 - 编码.(你最后一次需要编写一个发出"Hello,world"的程序是什么时候?)
TMP是关于在编译时执行算法的,这意味着一些明显的优势:
当然,也有缺点:
与往常一样,在每种情况下,您都必须权衡利弊.
至于一个更有用的示例:一旦掌握了类型列表和基于它们的基本编译时算法,您可能会理解以下内容:
typedef
type_list_generator< signed char
, signed short
, signed int
, signed long
>::result_type
signed_int_type_list;
typedef
type_list_find_if< signed_int_type_list
, exact_size_predicate<8>
>::result_type
int8_t;
typedef
type_list_find_if< signed_int_type_list
, exact_size_predicate<16>
>::result_type
int16_t;
typedef
type_list_find_if< signed_int_type_list
, exact_size_predicate<32>
>::result_type
int32_t;
Run Code Online (Sandbox Code Playgroud)
这是(几乎简化)我几周前写的实际代码.它将从类型列表中选择适当的类型,替换#ifdef便携式代码中常见的orgies.它不需要维护,无需适应您的代码可能需要移植到的每个平台,如果当前平台没有正确的类型,则会发出编译错误.
另一个例子是:
template< typename TFunc, typename TFwdIter >
typename func_traits<TFunc>::result_t callFunc(TFunc f, TFwdIter begin, TFwdIter end);
Run Code Online (Sandbox Code Playgroud)
给定一个函数f和一个字符串序列,这将剖析函数的签名,将序列中的字符串转换为正确的类型,并使用这些对象调用函数.它主要是TMP内部.