关于boost mpl占位符的推理

Olu*_*ide 4 c++ boost-mpl

Boost MPL库文档的教程:元函数和高阶元编程部分说明transform可以像这样调用

typename mpl::transform<D1,D2, mpl::minus<_1,_2> >::type
Run Code Online (Sandbox Code Playgroud)

其中占位符_1,并_2表示,当的变换BinaryOperation被调用时,它的第一个和第二个参数将在所示的位置传递给减_1_2分别.

我已经一遍又一遍地读了这个近一个月,我仍然不明白.

什么样的价值观究竟做占位符_1,并_2有?D1D2?如果是这样,为什么不写mpl::minus<D1,D2>?又考虑到占位符定义为 typedef arg<1> _1;typedef arg<2> _2;,因此原来的表达在我的脑海里

typename mpl::transform<D1,D2, mpl::minus<<arg<1>,<arg<2> > >::type
Run Code Online (Sandbox Code Playgroud)

我确定我正在考虑占位符错误的方式.我在这里感谢一些指导.

Quu*_*one 7

实际上,你正在以错误的方式思考占位符.

mpl::minus是MPL的元语言中的模板,其象征性地表示(或对应于)某种高级行为,即减法.你在想它就好像它是一个非元构造,比如一个函数

int minus(int a, int b) { return a - b; }
Run Code Online (Sandbox Code Playgroud)

但事实并非如此.(标准的C++ 11库确实有类似的东西,叫做std::minus<>,但那不是什么mpl::minus!)

mpl::minus 表示更高抽象级别的减法运算.不要担心另外两段关于如何mpl::minus实施的段落.试想它代表什么,这是两件事的减法.

啊,但是哪两件事?那么,mpl::minus让你指定那些东西作为模板参数.例如,

mpl::minus<mpl::int_<7>, mpl::int_<3>>
Run Code Online (Sandbox Code Playgroud)

扩展为其成员typedef type与其相同的类型mpl::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!
Run Code Online (Sandbox Code Playgroud)

同样,在元空间中,

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!
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,我们的意思是说

auto c = std::vector<int>{};
std::transform(a.begin(), a.end(), b.begin(), std::back_inserter(c), std::minus<>{});
Run Code Online (Sandbox Code Playgroud)

我们的意思是说,在第二个(meta)案例中,是

using c = mpl::transform<a, b, mpl::minus>::type;  // caveat: we're not done yet
Run Code Online (Sandbox Code Playgroud)

请注意,C++ 11 std::transform采用成对的迭代器a.begin(), a.end()而不仅仅是a; 它需要b.begin()但不是b.end()(缺陷现在只是由委员会纠正); 并且它通过输出迭代器进行变异 c,而不是返回一个全新的对象,以提高效率.MPL的编译时元版本直接获取容器ab返回一个新容器c,即它具有值语义,恕我直言更容易思考.

所以,以上都是正确的,除了一个小细节!mpl::transform实际上是一种非常通用的算法,这意味着它希望您详细说明转换的细节.你说" mpl::minus",意思是"减去",好吧,但减去什么呢?从第二个元素中减去第一个序列的元素?从第一个中减去第二个元素?42从第二个序列的元素中减去并完全抛出第一个序列?

嗯,我们的意思是"从第一个元素中减去第二个序列的元素." 我们写的是

using c = mpl::transform<a, b, mpl::minus<_1, _2>>::type;
Run Code Online (Sandbox Code Playgroud)

我们同样可以写

using c = mpl::transform<b, a, mpl::minus<_2, _1>>::type;
Run Code Online (Sandbox Code Playgroud)

- 这意味着完全相同的事情.

这种通用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;
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以a使用符号引用序列的相同元素三次_1,同时_2引用序列的相应元素b.

所以,这是该的符号_1,并_2在上下文mpl::transform.但你可能仍然想知道它们是如何实施的.嗯,这里没有魔力.它们也可以实现为

template<int> struct _ {};
using _1 = _<1>;
using _2 = _<2>;
Run Code Online (Sandbox Code Playgroud)

只要他们在C++的类型系统中获得独特的,可区分的实体,那就是MPL真正关心的.

但实际上它们实际上是作为特殊化的typedef实现的mpl::arg,这导致了一个漂亮的技巧.我们可以说,既然_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
                                ...
Run Code Online (Sandbox Code Playgroud)

而且我猜想mpl::transform能够在内部利用这个事实.

  • 进入疯狂之旅。很好的答案! (2认同)