Boost MPL库文档的Tutorial: Metafunctions and Higher-Order Metaprogramming部分指出transform
可以像这样调用
typename mpl::transform<D1,D2, mpl::minus<_1,_2> >::type
其中占位符_1
和_2
表示当调用转换的BinaryOperation
时,其第一个和第二个参数将在{{1}指示的位置传递给减号分别和_1
。
我已经一遍又一遍地读了这个,差不多一个月了,我仍然不明白。
占位符_2
和_1
具有哪些值? _2
和D1
?如果是这样,为什么不写D2
?同时考虑占位符为defined as mpl::minus<D1,D2>
和typedef arg<1> _1;
,因此我脑海中的原始表达式
typedef arg<2> _2;
我确定我正在以错误的方式思考占位符。我很欣赏这里的一些指导。
答案 0 :(得分:7)
的确,你以错误的方式思考占位符。
mpl::minus
是MPL的元语言中的模板,其符号表示(或对应于)某种高级行为,即减法。你正在考虑它,好像它是一个非元构造,如函数
int minus(int a, int b) { return a - b; }
但事实并非如此。 (标准的C ++ 11库确实有类似的东西,称为std::minus<>
,但 不 mpl::minus
确实!)
mpl::minus
代表 更高抽象级别的减法操作。不要担心另外两段关于mpl::minus
如何实施的段落。只要想想它代表什么,这就是减去两件事。
mpl::minus
允许您将这些内容指定为模板参数。例如,
mpl::minus<mpl::int_<7>, mpl::int_<3>>
扩展为其成员typedef type
与mpl::int_<4>
相同的类型。
好的,但在Boost的维度分析示例中,他们不仅仅有两件事;他们有维度的序列 D1
和D2
。 (这是非常重要的一点!)减去序列与减去整数是一回事;考虑
auto a = std::vector<int>{ 1, 0, 0 };
auto b = std::vector<int>{ 0, 1, 0 };
auto c = (a - b); // Won't compile!
同样,在元空间中,
using a = mpl::vector<mpl::int_<1>, mpl::int_<0>, mpl::int_<0>>;
using b = mpl::vector<mpl::int_<1>, mpl::int_<0>, mpl::int_<0>>;
using c = mpl::minus<a,b>; // Won't compile!
我们的意思在第一种情况下说
auto c = std::vector<int>{};
std::transform(a.begin(), a.end(), b.begin(), std::back_inserter(c), std::minus<>{});
在第二个(meta)案例中,以及我们的意思是
using c = mpl::transform<a, b, mpl::minus>::type; // caveat: we're not done yet
请注意,C ++ 11 std::transform
采用成对的迭代器a.begin(), a.end()
而不仅仅是a
;它需要b.begin()
但不需要b.end()
(a deficiency that is only now being corrected by the Committee);它通过输出迭代器变异 c
,而不是返回一个全新的对象,以提高效率。 MPL的编译时元版本直接获取容器a
和b
并返回一个新容器c
,即它具有值语义,恕我直言更容易思考约。
所以,上面的内容都是正确的, EXCEPT 一个细节! mpl::transform
实际上是一种非常通用的算法,这意味着它希望您详细说明转换的细节。你说&#34; mpl::minus
&#34;,这意味着&#34;减去&#34;,好吧,但减去什么?从第二个元素中减去第一个序列的元素?从第一个中减去第二个元素?从第二个序列的元素中减去42
并完全抛出第一个序列?
嗯,我们的意思是&#34;从第一个元素中逐个元素地减去第二个序列元素。&#34;我们写的是
using c = mpl::transform<a, b, mpl::minus<_1, _2>>::type;
我们同样可以写
using c = mpl::transform<b, a, mpl::minus<_2, _1>>::type;
- 这意味着完全相同的事情。
这种通用的transform
算法让我们可以编写复杂的转换,例如
// hide some irrelevant boilerplate behind an alias
template<typename... Ts>
using multiplies_t = mpl::multiplies<Ts...>::type;
// compute c = a^2 + 2ab + 1
using c = mpl::transform<a, b,
mpl::plus<multiplies_t< _1, _1 >, // a^2 ...
multiplies_t< mpl::int_<2>, _1, _2 >, // ... + 2ab ...
mpl::int_<1>> // ... + 1
>::type;
在这里,我们可以使用符号a
三次引用序列_1
的相同元素,而_2
引用序列b
的相应元素。 / p>
所以,这是_1
上下文中符号_2
和mpl::transform
的点。但是你可能仍然想知道他们是如何实施的。嗯,这里没有魔力。它们也可以实现为
template<int> struct _ {};
using _1 = _<1>;
using _2 = _<2>;
只要他们在C ++的类型系统中获得独特的,可区分的实体,那么所有MPL都非常关心。
但事实上,他们实际实现作为mpl::arg
特化的typedef实现,这导致了一个漂亮的技巧。由于_1
是mpl::arg<1>
的同义词,我们可以说
_1::apply<A,B,C>::type is the same type as A
_2::apply<A,B,C>::type is the same type as B
...
我猜我mpl::transform
能够在内部利用这个事实。