【问题标题】:Reasoning about boost mpl placeholders关于 boost mpl 占位符的推理
【发布时间】:2014-09-14 21:50:02
【问题描述】:

Boost MPL 库文档的Tutorial: Metafunctions and Higher-Order Metaprogramming 部分指出transform 可以像这样调用

typename mpl::transform<D1,D2, mpl::minus<_1,_2> >::type

其中占位符_1_2 表示当调用转换的BinaryOperation 时,其第一个和第二个参数将分别传递给_1_2 指示的位置的减号。

我已经反复阅读了将近一个月,但我仍然不明白。

占位符 _1_2 究竟有什么值? D1D2?如果是这样,为什么不写mpl::minus&lt;D1,D2&gt;?还考虑到占位符是 defined as typedef arg&lt;1&gt; _1;typedef arg&lt;2&gt; _2; 因此原始表达式 在我的脑海中相当于

typename mpl::transform<D1,D2, mpl::minus<<arg<1>,<arg<2> > >::type

我确定我以错误的方式考虑占位符。我会很感激这里的一些指导。

【问题讨论】:

    标签: c++ boost-mpl


    【解决方案1】:

    确实,您以错误的方式考虑占位符。

    mpl::minus 是 MPL 元语言中的一个模板,它象征性地表示(或对应于)某种高级行为,即减法。您将其视为非元构造,例如函数

    int minus(int a, int b) { return a - b; }
    

    但事实并非如此。 (标准 C++11 库确实有类似的东西,称为 std::minus&lt;&gt;,但 不是 mpl::minus 所做的!)

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

    啊,但是哪两件事呢?好吧,mpl::minus 允许您将这些内容指定为模板参数。例如,

    mpl::minus<mpl::int_<7>, mpl::int_<3>>
    

    扩展为成员 typedef typempl::int_&lt;4&gt; 相同的类型。

    好的,但是在 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<>{});
    

    在第二种(元)情况下,我们的意思

    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,即它具有值语义,恕我直言,这更容易考虑。

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

    嗯,我们的意思是“从第一个序列的元素中减去第二个序列的元素”。我们写成

    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的相同元素3次,使用符号_1,而_2指的是序列b的对应元素。

    所以,这就是mpl::transform 上下文中符号_1_2。但是您可能仍然想知道它们是如何实现的。好吧,这里没有魔法。它们也可以实现为

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

    只要它们在 C++ 的类型系统中获得唯一、可区分的实体,这就是 MPL 真正关心的一切。

    但实际上它们实际上被实现为 mpl::arg 的特化的 typedef,这导致了一个漂亮的技巧。由于_1mpl::arg&lt;1&gt; 的同义词,我们可以说

    _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 能够在内部利用这一事实。

    【讨论】:

    • 疯狂之旅又回来了。很好的答案!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多