关于boost mpl占位符的推理

时间:2014-09-14 21:50:03

标签: c++ boost-mpl

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具有哪些值? _2D1?如果是这样,为什么不写D2?同时考虑占位符为defined as mpl::minus<D1,D2>typedef arg<1> _1;,因此我脑海中的原始表达式

typedef arg<2> _2;

我确定我正在以错误的方式思考占位符。我很欣赏这里的一些指导。

1 个答案:

答案 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 typempl::int_<4>相同的类型。

好的,但在Boost的维度分析示例中,他们不仅仅有两件事;他们有维度的序列 D1D2。 (这是非常重要的一点!)减去序列与减去整数是一回事;考虑

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的编译时元版本直接获取容器ab并返回一个新容器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上下文中符号_2mpl::transform。但是你可能仍然想知道他们是如何实施的。嗯,这里没有魔力。它们也可以实现为

template<int> struct _ {};
using _1 = _<1>;
using _2 = _<2>;

只要他们在C ++的类型系统中获得独特的,可区分的实体,那么所有MPL都非常关心。

但事实上,他们实际实现作为mpl::arg特化的typedef实现,这导致了一个漂亮的技巧。由于_1mpl::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能够在内部利用这个事实。