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有?D1和D2?如果是这样,为什么不写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)
我确定我正在考虑占位符错误的方式.我在这里感谢一些指导.
实际上,你正在以错误的方式思考占位符.
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的维度分析示例中,他们没有两件事; 它们有序列 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!
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的编译时元版本直接获取容器a并b返回一个新容器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能够在内部利用这个事实.