Boost C++ 库

...世界上最受推崇、设计最专业的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

Boost.Mp11: C++11 元编程库 - Boost C++ 函数库

概述

Mp11 是一个 C++11 元编程库,用于编译时操作包含类型的数据结构。它基于模板别名和可变参数模板,并实现了文章“简单 C++ 元编程”及其续集中概述的方法。在继续阅读此文档之前,强烈建议阅读这些文章。

Mp11 构建的通用原则是算法和元函数是形如 `F` 的模板别名,数据结构是形如 `L` 的列表,库不对 `L` 施加任何要求。`mp_list` 是内置列表类型,但 `std::tuple`、`std::pair` 和 `std::variant` 也是完全合法的列表类型,尽管 `std::pair` 由于只有两个元素而不可调整大小,因此无法与需要添加或删除元素的算法一起使用。

这种方法的另一个显著特点是,列表(`L`)与元函数(`F`)具有相同的形式,因此可以作为元函数使用。例如,通过 `mp_transform>` 将 `std::add_pointer_t` 应用于列表 `std::tuple` 会得到 `std::tuple`,但我们也可以将 `mp_list` 应用于相同的元组

using R = mp_transform<mp_list, std::tuple<int, float>>;

并得到 `std::tuple, mp_list>`。

定义

一个 列表 是一个(通常但不一定可变参数的)模板类,其参数都是类型,例如 `mp_list`、`mp_list<>`、`std::tuple`、`std::pair`、`std::shared_ptr`。

一个 元函数 是一个类模板或一个模板别名,其参数都是类型,例如 `std::add_pointer_t`、`std::is_const`、`mp_second`、`mp_push_front`、`mp_list`、`std::tuple`、`std::pair`、`std::shared_ptr`,或者

template<class...> using F1 = void;

template<class T> using F2 = T*;

template<class... T> using F3 = std::integral_constant<std::size_t, sizeof...(T)>;

一个 带引号的元函数 是一个类,它有一个名为 `fn` 的公共元函数成员,例如

struct Q1 { template<class...> using fn = void; };

struct Q2 { template<class T> using fn = T*; };

struct Q3 { template<class... T> using fn =
  std::integral_constant<std::size_t, sizeof...(T)>; };

一个 整型常量类型 是一个带有公共成员 `value` 的类,该成员是 C++ 意义上的整型常量。例如,`std::integral_constant`,或

struct N { static int constexpr value = 2; };

一个 集合 是一个元素唯一的列表。

一个 映射 是一个列表的列表,内部列表至少有一个元素(键)。映射的键必须是唯一的。例如,

using M1 = std::tuple<std::pair<int, int*>, std::pair<float, float*>,
    std::pair<void, void*>>;

using M2 = mp_list<mp_list<int, int*>, mp_list<float>,
    mp_list<char, char[1], char[2]>>;

一个 值列表 是一个模板类,其参数都是值(非类型模板参数)。值列表匹配 `template class L`,并且需要 C++17(因为 `auto` 模板参数是 C++17 的一个特性)。

值列表只被少数几个原语支持。Mp11 主要关注类型操作。对于处理值列表,通常的方法是使用 `mp_rename` 将值列表转换为类型列表,操作类型列表,然后使用 `mp_rename_v` 转换回值列表。

示例

生成测试用例

假设我们已经编写了一个元函数 `result`

template<class T> using promote = typename std::common_type<T, int>::type;

template<class T, class U> using result =
    typename std::common_type<promote<T>, promote<U>>::type;

它应该表示整数类型 `T` 和 `U` 上的算术运算结果,例如 `t + u`。我们想测试 `result` 对于 `T` 和 `U` 的各种组合是否给出正确的结果,所以我们编写函数

template<class T1, class T2> void test_result()
{
    using T3 = decltype( T1() + T2() );
    using T4 = result<T1, T2>;

    std::cout << ( std::is_same<T3, T4>::value? "[PASS]": "[FAIL]" ) << std::endl;
}

然后需要调用它大量的次数

int main()
{
    test_result<char, char>();
    test_result<char, short>();
    test_result<char, int>();
    test_result<char, unsigned>();
    // ...
}

手动编写所有这些类型组合是笨拙的、容易出错的,最糟糕的是,乏味。我们可以利用 Mp11 来自动化这项任务

#include <boost/mp11.hpp>
#include <boost/core/demangle.hpp>
#include <type_traits>
#include <iostream>
#include <typeinfo>

using namespace boost::mp11;

template<class T> std::string name()
{
    return boost::core::demangle( typeid(T).name() );
}

template<class T> using promote = typename std::common_type<T, int>::type;

template<class T, class U> using result =
    typename std::common_type<promote<T>, promote<U>>::type;

template<class T1, class T2> void test_result( mp_list<T1, T2> const& )
{
    using T3 = decltype( T1() + T2() );
    using T4 = result<T1, T2>;

    std::cout << ( std::is_same<T3, T4>::value? "[PASS] ": "[FAIL] " )
        << name<T1>() << " + " << name<T2>() << " -> " << name<T3>()
        << ", result: " << name<T4>() << std::endl;
}

int main()
{
    using L = std::tuple<char, short, int, unsigned, long, unsigned long>;
    tuple_for_each( mp_product<mp_list, L, L>(), [](auto&& x){ test_result(x); } );
}

它是如何工作的?

`mp_product` 调用 `F`,其中 `T1` 遍历 `L1` 的元素,`T2` 遍历 `L2` 的元素,就像执行两个嵌套循环一样。然后它返回这些结果的列表,类型与 `L1` 相同。

在我们的例子中,两个列表都是相同的 `std::tuple`,而 `F` 是 `mp_list`,所以 `mp_product` 将给我们 `std::tuple, mp_list, mp_list, ... , mp_list, mp_list>`。

然后我们默认构造这个元组并将其传递给 `tuple_for_each`。`tuple_for_each(tp, f)` 为每个元组元素调用 `f`;我们使用一个(C++14)lambda 来调用 `test_result`。

在纯 C++11 中,我们不能使用带有 `auto&&` 参数的 lambda,所以我们必须将 `test_result` 转换为一个带有模板 `operator()` 的函数对象,并直接将其传递给 `tuple_for_each`

struct test_result
{
    template<class T1, class T2> void operator()( mp_list<T1, T2> const& ) const
    {
        using T3 = decltype( T1() + T2() );
        using T4 = result<T1, T2>;

        std::cout << ( std::is_same<T3, T4>::value? "[PASS] ": "[FAIL] " )
            << name<T1>() << " + " << name<T2>() << " -> " << name<T3>()
            << ", result: " << name<T4>() << std::endl;
    }
};

int main()
{
    using L = std::tuple<char, short, int, unsigned, long, unsigned long>;
    tuple_for_each( mp_product<mp_list, L, L>(), test_result() );
}

编写 common_type 特化

标准 trait `std::common_type` 用于获取一个类型,所有参数都可以转换为该类型而不会不必要地丢失精度,当其默认实现(基于三元 `?:` 运算符)不适用时,用户可以对其进行特化。

让我们为两个 `std::tuple` 参数编写一个 `common_type` 特化。为此,我们需要一个元函数,它将 `std::common_type` 应用于每对元素并将结果收集到元组中

template<class... T> using common_type_t =
    typename std::common_type<T...>::type; // standard in C++14

template<class Tp1, class Tp2> using common_tuple =
    mp_transform<common_type_t, Tp1, Tp2>;

然后特化 `common_type` 以使用它

namespace std
{

    template<class... T1, class... T2>
    struct common_type<std::tuple<T1...>, std::tuple<T2...>>:
        mp_defer<common_tuple, std::tuple<T1...>, std::tuple<T2...>>
    {
    };

} // std

(无需为两个以上参数特化 `std::common_type` - 它会从二元情况合成适当的语义。)

这里的微妙之处在于 `mp_defer` 的使用。我们本可以为 `common_tuple, std::tuple>` 定义一个嵌套的 `type`,并且在所有有效情况下它仍然可以工作。然而,通过让 `mp_defer` 定义 `type`,我们使我们的特化 SFINAE 友好

也就是说,当我们的 `common_tuple` 导致替换失败而不是硬错误时,`mp_defer` 将不会定义嵌套的 `type`,而 `common_type_t`(定义为 `typename common_type<...>::type`)也将导致替换失败。

再举一个例子,考虑假设类型 `expected`,它表示成功返回一个 `T` 类型的值,或者不成功返回一个列表 `E...` 中某种类型的错误代码。`expected` 和 `expected` 的共同类型是 `expected, E1, E2, E3, E4, E5>`。也就是说,可能的返回值合并为它们的共同类型,并且我们取错误类型集合的并集。

因此,

template<class T1, class E1, class T2, class E2> using common_expected =
    mp_rename<mp_push_front<mp_unique<mp_append<E1, E2>>, common_type_t<T1, T2>>,
        expected>;

namespace std
{

    template<class T1, class... E1, class T2, class... E2>
    struct common_type<expected<T1, E1...>, expected<T2, E2...>>:
        mp_defer<common_expected, T1, mp_list<E1...>, T2, mp_list<E2...>>
    {
    };

} // std

这里我们采取了不同的方法;我们不是将 `expected` 类型传递给 `common_expected`,而是传递 `T` 类型和 `E` 类型的列表。这使我们的工作更容易。`mp_unique>` 给了我们 `E1` 和 `E2` 的连接,并删除了重复项;然后我们通过 `mp_push_front` 将 `common_type_t` 添加到前面;最后,我们将生成的 `mp_list` `mp_rename` 为 `expected`。

修复 tuple_cat

文章简单 C++11 元编程构建了标准函数 `tuple_cat` 的实现,最终结果如下

template<class L> using F = mp_iota<mp_size<L>>;

template<class R, class...Is, class... Ks, class Tp>
R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
{
    return R{ std::get<Ks::value>(std::get<Is::value>(tp))... };
}

template<class... Tp,
    class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
    R tuple_cat( Tp &&... tp )
{
    std::size_t const N = sizeof...(Tp);

    // inner

    using list1 = mp_list<
        mp_rename<typename std::remove_reference<Tp>::type, mp_list>...>;

    using list2 = mp_iota_c<N>;

    using list3 = mp_transform<mp_fill, list1, list2>;

    using inner = mp_apply<mp_append, list3>;

    // outer

    using list4 = mp_transform<F, list1>;

    using outer = mp_apply<mp_append, list4>;

    //

    return tuple_cat_<R>( inner(), outer(),
        std::forward_as_tuple( std::forward<Tp>(tp)... ) );
}

然而,这个函数并不完全正确,因为它不能正确处理某些情况。例如,尝试连接包含仅可移动元素(如 `unique_ptr`)的元组会失败

std::tuple<std::unique_ptr<int>> t1;
std::tuple<std::unique_ptr<float>> t2;

auto result = ::tuple_cat( std::move( t1 ), std::move( t2 ) );

尝试连接 `const` 元组会失败

std::tuple<int> const t1;
std::tuple<float> const t2;

auto result = ::tuple_cat( t1, t2 );

最后,标准 `tuple_cat` 被指定用于任意类似元组的类型(即,所有支持 `tuple_size`、`tuple_element` 和 `get` 的类型),而我们的实现仅适用于 `tuple` 和 `pair`。例如,`std::array` 会失败

std::array<int, 2> t1{ 1, 2 };
std::array<float, 3> t2{ 3.0f, 4.0f, 5.0f };

auto result = ::tuple_cat( t1, t2 );

让我们逐一修复这些问题。对仅移动类型的支持很容易,如果您知道在哪里寻找的话。问题在于我们传递给辅助 `tuple_cat_` 的 `Tp` 是(正确地)`tuple&&, unique_ptr&&>`,但是 `std::get<0>(tp)` 仍然返回 `unique_ptr&`,因为 `tp` 是一个左值。这种行为有点令人惊讶,但旨在防止意外的双重移动。

长话短说,我们需要在 `tuple_cat_` 中使用 `std::move(tp)` 来使 `tp` 成为一个右值。

template<class R, class...Is, class... Ks, class Tp>
R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
{
    return R{ std::get<Ks::value>(std::get<Is::value>(std::move(tp)))... };
}

接下来,`const` 限定的元组。这里的问题是,我们从输入元组中剥离了引用,但没有剥离 `const`。结果,我们尝试使用 Mp11 算法操作 `tuple const` 等类型,而这些类型不符合列表概念。我们只需要通过定义标准库中莫名其妙地缺失的有用 `remove_cv_ref` 原语来剥离限定符

template<class T> using remove_cv_ref = typename std::remove_cv<
    typename std::remove_reference<T>::type>::type;

然后用 `remove_cv_ref` 代替 `typename std::remove_reference::type`

template<class... Tp,
    class R = mp_append<std::tuple<>, remove_cv_ref<Tp>...>>
    R tuple_cat( Tp &&... tp )
{
    std::size_t const N = sizeof...(Tp);

    // inner

    using list1 = mp_list<mp_rename<remove_cv_ref<Tp>, mp_list>...>;

    // ...

最后,类元组类型。我们到目前为止已经利用了 `std::pair` 和 `std::tuple` 是有效的 Mp11 列表这一事实,但通常情况下,任意类元组类型不是,所以我们需要将它们转换为此类。为此,我们需要定义一个元函数 `from_tuple_like`,它将接受一个任意的类元组类型,并在我们的例子中返回相应的 `mp_list`。

从技术上讲,更符合原则的方法是返回 `std::tuple`,但在这里 `mp_list` 会更方便。

我们需要的是,给定一个类元组类型 `Tp`,获取 `mp_list::type, std::tuple_element<1, Tp>::type, ... , std::tuple_element::type>`,其中 `N` 是 `tuple_size::value`。以下是一种实现方法

template<class T, class I> using tuple_element =
    typename std::tuple_element<I::value, T>::type;

template<class T> using from_tuple_like =
    mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>;

(`mp_iota` 是一个算法,它返回一个包含 `mp_size_t<0>`、`mp_size_t<1>`、...、`mp_size_t` 元素的 `mp_list`。)

请记住,`mp_product` 执行与两个嵌套循环等效的操作,遍历 `L1` 和 `L2` 的元素,将 `F` 应用于两个变量并收集结果。在我们的例子中,`L1` 包含单个元素 `T`,因此只剩下第二个循环(遍历 `mp_iota`,其中 `N` 是 `tuple_size`),我们得到一个与 `L1` 类型相同(一个 `mp_list`)的列表,其中包含 `tuple_element>`、`tuple_element>`、...、`tuple_element>`。

为了完整起见,这里还有另一种更传统的方法来达到相同的结果

template<class T> using from_tuple_like =
    mp_transform_q<mp_bind_front<tuple_element, T>, mp_iota<std::tuple_size<T>>>;

应用了所有这些修复后,我们完全可操作的 `tuple_cat` 现在看起来像这样

template<class L> using F = mp_iota<mp_size<L>>;

template<class R, class...Is, class... Ks, class Tp>
R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
{
    return R{ std::get<Ks::value>(std::get<Is::value>(std::move(tp)))... };
}

template<class T> using remove_cv_ref = typename std::remove_cv<
    typename std::remove_reference<T>::type>::type;

template<class T, class I> using tuple_element =
    typename std::tuple_element<I::value, T>::type;

template<class T> using from_tuple_like =
    mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>;

template<class... Tp,
    class R = mp_append<std::tuple<>, from_tuple_like<remove_cv_ref<Tp>>...>>
    R tuple_cat( Tp &&... tp )
{
    std::size_t const N = sizeof...(Tp);

    // inner

    using list1 = mp_list<from_tuple_like<remove_cv_ref<Tp>>...>;
    using list2 = mp_iota_c<N>;

    using list3 = mp_transform<mp_fill, list1, list2>;

    using inner = mp_apply<mp_append, list3>;

    // outer

    using list4 = mp_transform<F, list1>;

    using outer = mp_apply<mp_append, list4>;

    //

    return tuple_cat_<R>( inner(), outer(),
        std::forward_as_tuple( std::forward<Tp>(tp)... ) );
}

计算返回类型

C++17 有一个标准变体类型,称为 `std::variant`。它还定义了一个函数模板 `std::visit`,可用于将函数应用于一个或多个变体中包含的值。因此,例如,如果变体 `v1` 包含 `1`,变体 `v2` 包含 `2.0f`,`std::visit(f, v1, v2)` 将调用 `f(1, 2.0f)`。

然而,`std::visit` 有一个限制:除非函数的所有可能应用都具有相同的返回类型,否则它不能返回结果。例如,如果 `v1` 和 `v2` 都是 `std::variant` 类型,

std::visit( []( auto const& x, auto const& y ){ return x + y; }, v1, v2 );

将无法编译,因为 `x + y` 的结果可以是 `int` 或 `float`,具体取决于 `v1` 和 `v2` 持有的内容。

一个可以容纳 `int` 或 `float` 的类型已经存在,令人惊讶的是,它被称为 `std::variant`。让我们编写自己的函数模板 `rvisit`,它与 `visit` 相同,但返回一个 `variant`

template<class F, class... V> auto rvisit( F&& f, V&&... v )
{
    using R = /*...*/;

    return std::visit( [&]( auto&&... x )
        { return R( std::forward<F>(f)( std::forward<decltype(x)>(x)... ) ); },
        std::forward<V>( v )... );
}

这基本上是调用 `std::visit` 来完成工作,但不是将 `f` 传递给它,而是传递一个 lambda,它执行与 `f` 相同的功能,只是它将结果转换为通用类型 `R`。`R` 应该是一个 `std::variant<...>`,其中省略号表示调用 `f` 与所有可能的变体值组合的返回类型。

我们首先定义一个辅助引用的元函数 `Qret`,它返回 `F` 应用于类型为 `T...` 的参数的结果

template<class F> struct Qret
{
    template<class... T> using fn =
        decltype( std::declval<F>()( std::declval<T>()... ) );
};

事实证明,C++17 已经包含了一个元函数,它返回函数 `F` 应用于类型为 `T...` 的参数的结果:`std::invoke_result_t`。我们可以利用它来简化我们的 `Qret` 为

template<class F> struct Qret
{
    template<class... T> using fn = std::invoke_result_t<F, T...>;
};

在 Mp11 中可以更简洁地表示为

using Qret = mp_bind_front<std::invoke_result_t, F>;

有了 `Qret`,可能返回类型的 `variant` 只是将其应用于变体值的可能组合的问题

using R = mp_product_q<Qret, remove_cv_ref<V>...>;

为什么这会起作用?`mp_product, L2, ..., Ln>` 返回 `L1, ...>`,其中 `Ui` 遍历列表的所有可能组合。由于在我们的例子中,所有 `Li` 都是 `std::variant`,结果也将是 `std::variant`。(`mp_product_q` 与 `mp_product` 相同,但适用于像我们的 `Qret` 这样的引用元函数。)

还剩一步。假设如上所述,我们传递两个类型为 `std::variant` 的变体,并且 `F` 是 `[]( auto const& x, auto const& y ){ return x + y; }`。这将生成长度为 9 的 `R`,每个组合一个,但其中许多元素将是相同的,要么是 `int`,要么是 `float`,我们需要过滤掉重复项。所以,我们将结果传递给 `mp_unique`

using R = mp_unique<mp_product_q<Qret, remove_cv_ref<V>...>>;

大功告成

#include <boost/mp11.hpp>
#include <boost/core/demangle.hpp>
#include <variant>
#include <type_traits>
#include <typeinfo>
#include <iostream>

using namespace boost::mp11;

template<class T> using remove_cv_ref = typename std::remove_cv<
    typename std::remove_reference<T>::type>::type;

template<class F, class... V> auto rvisit( F&& f, V&&... v )
{
    using Qret = mp_bind_front<std::invoke_result_t, F>;

    using R = mp_unique<mp_product_q<Qret, remove_cv_ref<V>...>>;

    return std::visit( [&]( auto&&... x )
        { return R( std::forward<F>(f)( std::forward<decltype(x)>(x)... ) ); },
        std::forward<V>( v )... );
}

template<class T> std::string name()
{
    return boost::core::demangle( typeid(T).name() );
}

template<class V> void print_variant( char const * n, V const& v )
{
    std::cout << "(" << name<decltype(v)>() << ")" << n << ": ";

    std::visit( []( auto const& x )
        { std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v );
}

int main()
{
    std::variant<char, int, float> v1( 1 );

    print_variant( "v1", v1 );

    std::variant<short, int, double> const v2( 3.14 );

    print_variant( "v2", v2 );

    auto v3 = rvisit( []( auto const& x, auto const& y ){ return x + y; }, v1, v2 );

    print_variant( "v3", v3 );
}

修订历史

1.88.0 版中的更改

  • 修复了非整数值的 `mp_from_sequence`(由于支持偏移量,在 1.83.0 中意外损坏)

1.87.0 版中的更改

  • 添加了 `mp_lambda`(由 Joaquin M Lopez Munoz 贡献)

1.85.0 版的更改

  • 添加了 `mp_sliding_fold`,它是 `mp_pairwise_fold` 的泛化(由 Braden Ganetsky 贡献)

  • 增加了 `mp_slice`(感谢 Braden Ganetsky)

  • 为 `mp_min_element`、`mp_max_element` 添加了值列表支持。

  • 为 `mp_transform` 添加了有限的值列表支持。

1.83.0 版的更改

  • 为 `mp_from_sequence`、`mp_iota`、`mp_iota_c` 添加了 offset/from 参数。

  • 增加了 `mp_value`、`mp_list_v`、`mp_rename_v`、`mp_is_value_list`。

  • 为 `` 中的原语添加了值列表支持。

  • 为 `mp_repeat`、`mp_fill`、`mp_at`、`mp_back`、`mp_take`、`mp_pop_back`、`mp_drop`、`mp_insert`、`mp_erase` 添加了值列表支持。

1.79.0 版中的更改

  • 添加了 `mp_valid_and_true`(由 Dmitry Arkhipov 贡献)

1.78.0 版的更改

  • 为 `mp_compose` 添加了对 n 元函数的支持(由 Dmitry Arkhipov 贡献)

1.77.0 版的更改

  • 增加了 `mp_intersperse`、`mp_split`、`mp_join`

1.75.0 版的更改

  • 增加了 `mp_pairwise_fold`(由 Barry Revzin 建议)

  • 移除了 `mp_invoke`(使用 `mp_invoke_q`)

1.74.0 版中的更改

  • 改进了 `mp_with_index` 在大 `N` 值时的编译性能

  • 添加了 `tuple_transform`(由 Hans Dembinski 贡献)

1.73.0 版的更改

  • 添加了 `mp_unique_if`(由 Kris Jusiak 贡献)

  • 添加了 `mp_flatten`

  • 添加了 `mp_rotate_left`、`mp_rotate_right`(由 Duncan Barber 贡献)

  • 添加了 `mp_compose`

  • 添加了 `mp_power_set`

  • 添加了 `mp_partial_sum`

  • 添加了 `mp_iterate`

1.70.0 版的更改

  • 将 `mp_invoke` 重命名为 `mp_invoke_q`

  • 添加了 `mp_similar`

  • 添加了 `mp_set_union`、`mp_set_intersection`、`mp_set_difference`

  • 添加了 `mp_not_fn`

  • 添加了 `mp_transform_first`、`mp_transform_second`、`mp_transform_third`

  • 添加了 `mp_filter`

  • 添加了 `mp_eval_if_not`、`mp_eval_or`、`mp_valid_q`

  • 添加了 `mp_back`、`mp_pop_back`

  • 添加了 `BOOST_MP11_VERSION`

1.69.0 版的更改

  • 移除了对 Boost.Config 的依赖;Mp11 现在是独立的

  • 改进了 `mp_with_index` 的代码生成

  • 添加了 `mp_starts_with`(由 Glen Fernandes 贡献)

  • 增加了 CMake 支持

参考

库的内容位于命名空间 `boost::mp11` 中。

整型常量, <boost/mp11/integral.hpp>

对于 Mp11 整型常量类型 `T`,`T::value` 是 C++ 意义上的整型常量。

mp_bool<B>

template<bool B> using mp_bool = std::integral_constant<bool, B>;

与 C++17 中的 `std::bool_constant` 相同。

mp_true

using mp_true = mp_bool<true>;

与 `std::true_type` 相同。

mp_false

using mp_false = mp_bool<false>;

与 `std::false_type` 相同。

mp_to_bool<T>

template<class T> using mp_to_bool = mp_bool<static_cast<bool>(T::value)>;

mp_not<T>

template<class T> using mp_not = mp_bool< !T::value >;

mp_int<I>

template<int I> using mp_int = std::integral_constant<int, I>;

mp_size_t<N>

template<std::size_t N> using mp_size_t = std::integral_constant<std::size_t, N>;

mp_value<A>

template<auto A> using mp_value = std::integral_constant<decltype(A), A>;

当值列表转换为类型列表时,无论是通过 `mp_rename` 显式转换,还是通过直接支持值列表的原语隐式转换,其值都会通过用 `mp_value` 包装来转换为类型。

需要 C++17。

列表操作, <boost/mp11/list.hpp>

mp_list<T…​>

template<class... T> struct mp_list {};

`mp_list` 是 Mp11 的标准列表类型,尽管该库不限于它,并且可以操作任意类模板,例如 `std::tuple` 或 `std::variant`。即使是 `std::pair` 也可以使用,如果转换不改变列表中元素的数量。

mp_list_c<T, I…​>

template<class T, T... I> using mp_list_c =
    mp_list<std::integral_constant<T, I>...>;

`mp_list_c` 生成一个 `mp_list`,其中包含与其整数模板参数对应的 `std::integral_constant` 类型。

代码示例 1. 使用 mp_list_c
using L1 = mp_list_c<int, 2, 3>; // mp_list<mp_int<2>, mp_int<3>>

mp_list_v<A…​>

template<auto... A> struct mp_list_v {};

Mp11 的标准值列表类型。需要 C++17。

mp_is_list<L>

template<class L> using mp_is_list = /*...*/;

如果 `L` 是一个列表(一个类模板的实例化,其模板参数是类型),`mp_is_list` 为 `mp_true`,否则为 `mp_false`。

mp_is_value_list<L>

template<class L> using mp_is_value_list = /*...*/;

在 C++17 下,如果 `L` 是一个值列表(一个类模板的实例化,其模板参数都是值),`mp_is_value_list` 为 `mp_true`,否则为 `mp_false`。

mp_size<L>

template<class L> using mp_size = /*...*/;

`mp_size` 返回列表 `L` 中元素的数量,类型为 `mp_size_t`。换句话说,`mp_size>` 是 `mp_size_t` 的别名。

在 C++17 下,支持将值列表作为 `L`。

代码示例 2. 将 mp_size 与 mp_list 一起使用
using L1 = mp_list<>;
using R1 = mp_size<L1>; // mp_size_t<0>
代码示例 3. 将 mp_size 与 std::pair 一起使用
using L2 = std::pair<int, int>;
using R2 = mp_size<L2>; // mp_size_t<2>
代码示例 4. 将 mp_size 与 std::tuple 一起使用
using L3 = std::tuple<float>;
using R3 = mp_size<L3>; // mp_size_t<1>
代码示例 5. 将 mp_size 与 mp_list_v 一起使用
using L4 = mp_list_v<1, false, 8ull>;
using R4 = mp_size<L4>; // mp_size_t<3>

mp_empty<L>

template<class L> using mp_empty = mp_bool<mp_size<L>::value == 0>;

如果列表 `L` 为空,`mp_empty` 是 `mp_true` 的别名,否则为 `mp_false`。

在 C++17 下,支持将值列表作为 `L`。

代码示例 6. 将 mp_empty 与 std::tuple 一起使用
using L1 = std::tuple<float>;
using R1 = mp_empty<L1>; // mp_false

using L2 = std::tuple<>;
using R2 = mp_empty<L2>; // mp_true

mp_assign<L1, L2>

template<class L1, class L2> using mp_assign = /*...*/;

`mp_assign, L2>` 是 `L1` 的别名。也就是说,它将 `L1` 的元素替换为 `L2` 的元素。

在 C++17 下,支持将值列表作为 `L1` 或 `L2`。当将值赋值给类型时,将其包装在 `mp_value` 中。当将类型赋值给值时,使用 `T::value` 解包。

代码示例 7. 将 mp_assign 与 mp_list 和 std::tuple 一起使用
using L1 = std::tuple<long>;
using L2 = mp_list<int, float>;

using R1 = mp_assign<L1, L2>; // std::tuple<int, float>
代码示例 8. 将 mp_assign 与 mp_list 和 std::pair 一起使用
using L1 = std::pair<long, char>;
using L2 = mp_list<int, float>;

using R1 = mp_assign<L1, L2>; // std::pair<int, float>
代码示例 9. 将 mp_assign 与值列表一起使用
using L1 = mp_list<int, float>;
using L2 = mp_list_v<0, false>;

using R1 = mp_assign<L1, L2>; // mp_list<mp_int<0>, mp_false>

mp_clear<L>

template<class L> using mp_clear = mp_assign<L, mp_list<>>;

`mp_clear>` 是 `L<>` 的别名,也就是说,它删除 `L` 的元素。

在 C++17 下,支持将值列表作为 `L`。

代码示例 10. 将 mp_clear 与 std::tuple 一起使用
using L1 = std::tuple<int, float>;
using R1 = mp_clear<L1>; // std::tuple<>
代码示例 11. 将 mp_clear 与 mp_list_v 一起使用
using L1 = mp_list_v<0, true>;
using R1 = mp_clear<L1>; // mp_list_v<>

mp_front<L>

template<class L> using mp_front = /*...*/;

`mp_front` 是列表 `L` 的第一个元素。也就是说,`mp_front>` 是 `T1` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,返回的元素将用 `mp_value` 包装。

代码示例 12. 将 mp_front 与 std::pair 一起使用
using L1 = std::pair<int, float>;
using R1 = mp_front<L1>; // int
代码示例 13. 将 mp_front 与 std::tuple 一起使用
using L2 = std::tuple<float, double, long double>;
using R2 = mp_front<L2>; // float
代码示例 14. 将 mp_front 与 mp_list 一起使用
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_front<L3>; // char[1]
代码示例 15. 将 mp_front 与 mp_list_v 一起使用
using L4 = mp_list_v<1, 2, 3, 4>;
using R4 = mp_front<L4>; // mp_int<1>

mp_pop_front<L>

template<class L> using mp_pop_front = /*...*/;

`mp_pop_front` 移除列表 `L` 的第一个元素。也就是说,`mp_pop_front>` 是 `L` 的别名。

在 C++17 下,支持将值列表作为 `L`。

代码示例 16. 将 mp_pop_front 与 std::tuple 一起使用
using L1 = std::tuple<float, double, long double>;
using R1 = mp_pop_front<L1>; // std::tuple<double, long double>
代码示例 17. 将 mp_pop_front 与 mp_list 一起使用
using L2 = mp_list<void>;
using R2 = mp_pop_front<L2>; // mp_list<>
代码示例 18. 将 mp_pop_front 与 mp_list_v 一起使用
using L3 = mp_list_v<1, 2, 3, 4>;
using R3 = mp_pop_front<L3>; // mp_list_v<2, 3, 4>

mp_first<L>

template<class L> using mp_first = mp_front<L>;

`mp_first` 是 `mp_front` 的另一个名称。

mp_rest<L>

template<class L> using mp_rest = mp_pop_front<L>;

`mp_rest` 是 `mp_pop_front` 的另一个名称。

mp_second<L>

template<class L> using mp_second = /*...*/;

`mp_second` 是列表 `L` 的第二个元素。也就是说,`mp_second>` 是 `T2` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,返回的元素将用 `mp_value` 包装。

代码示例 19. 将 mp_second 与 std::pair 一起使用
using L1 = std::pair<int, float>;
using R1 = mp_second<L1>; // float
代码示例 20. 将 mp_second 与 std::tuple 一起使用
using L2 = std::tuple<float, double, long double>;
using R2 = mp_second<L2>; // double
代码示例 21. 将 mp_second 与 mp_list 一起使用
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_second<L3>; // char[2]
代码示例 22. 将 mp_second 与 mp_list_v 一起使用
using L4 = mp_list_v<1, 2, 3, 4>;
using R4 = mp_second<L4>; // mp_int<2>

mp_third<L>

template<class L> using mp_third = /*...*/;

`mp_third` 是列表 `L` 的第三个元素。也就是说,`mp_third>` 是 `T3` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,返回的元素将用 `mp_value` 包装。

代码示例 23. 将 mp_third 与 std::tuple 一起使用
using L1 = std::tuple<float, double, long double>;
using R1 = mp_third<L1>; // long double
代码示例 24. 将 mp_third 与 mp_list 一起使用
using L2 = mp_list<char[1], char[2], char[3], char[4]>;
using R2 = mp_third<L2>; // char[3]
代码示例 25. 将 mp_third 与 mp_list_v 一起使用
using L3 = mp_list<1, 2, 3, 4>;
using R3 = mp_third<L3>; // mp_int<3>

mp_push_front<L, T…​>

template<class L, class... T> using mp_push_front = /*...*/;

`mp_push_front` 在列表 `L` 的前面插入元素 `T...`。也就是说,`mp_push_front, T...>` 是 `L` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,`mp_push_front, T...>` 是 `L`。

代码示例 26. 将 mp_push_front 与 std::tuple 一起使用
using L1 = std::tuple<double, long double>;
using R1 = mp_push_front<L1, float>; // std::tuple<float, double, long double>
代码示例 27. 将 mp_push_front 与 mp_list 一起使用
using L2 = mp_list<void>;
using R2 = mp_push_front<L2, char[1], char[2]>; // mp_list<char[1], char[2], void>
代码示例 28. 将 mp_push_front 与 mp_list_v 一起使用
using L3 = mp_list_v<0, 1>;
using R3 = mp_push_front<L3, mp_true, mp_false>; // mp_list_v<true, false, 0, 1>

mp_push_back<L, T…​>

template<class L, class... T> using mp_push_back = /*...*/;

`mp_push_back` 在列表 `L` 的末尾插入元素 `T...`。也就是说,`mp_push_back, T...>` 是 `L` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,`mp_push_back, T...>` 是 `L`。

代码示例 29. 将 mp_push_back 与 std::tuple 一起使用
using L1 = std::tuple<double, long double>;
using R1 = mp_push_back<L1, float>; // std::tuple<double, long double, float>
代码示例 30. 将 mp_push_back 与 mp_list 一起使用
using L2 = mp_list<void>;
using R2 = mp_push_back<L2, char[1], char[2]>; // mp_list<void, char[1], char[2]>
代码示例 31. 将 mp_push_back 与 mp_list_v 一起使用
using L3 = mp_list_v<0, 1>;
using R3 = mp_push_front<L3, mp_true, mp_false>; // mp_list_v<0, 1, true, false>

mp_rename<L, Y>

template<class L, template<class...> class Y> using mp_rename = /*...*/;

`mp_rename` 将列表 `L` 的类型更改为 `Y`。也就是说,`mp_rename, Y>` 是 `Y` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,`mp_rename, Y>` 是 `Y...>`。

代码示例 32. 使用 mp_rename 将 std::pair 重命名为 std::tuple
using L1 = std::pair<double, long double>;
using R1 = mp_rename<L1, std::tuple>; // std::tuple<double, long double>
代码示例 33. 使用 mp_rename 将 std::tuple 重命名为 mp_list
using L2 = std::tuple<void>;
using R2 = mp_rename<L2, mp_list>; // mp_list<void>
代码示例 34. 使用 mp_rename 将值列表转换为类型列表
using L3 = mp_list_v<false, 7>;
using R3 = mp_rename<L3, mp_list>; // mp_list<mp_false, mp_int<7>>

mp_apply<F, L>

template<template<class...> class F, class L> using mp_apply = mp_rename<L, F>;

`mp_apply` 将元函数 `F` 应用于列表 `L` 的内容,也就是说,`mp_apply>` 是 `F` 的别名。(`mp_apply` 与 `mp_rename` 相同,但参数顺序相反。)

代码示例 35. 将 mp_apply 与 std::pair 一起使用
using L1 = std::pair<double, long double>;
using R1 = mp_apply<std::is_same, L1>; // std::is_same<double, long double>

mp_apply_q<Q, L>

template<class Q, class L> using mp_apply_q = mp_apply<Q::template fn, L>;

与 `mp_apply` 相同,但接受一个引用元函数。

代码示例 36. 将 mp_apply_q 与 mp_bind_front 一起使用
using L1 = std::tuple<double, long double>;
using L2 = mp_list<int, long>;

using R1 = mp_apply_q<mp_bind_front<mp_push_back, L1>, L2>;
  // R1 is std::tuple<double, long double, int, long>

mp_rename_v<L, Y>

template<class L, template<auto...> class Y> using mp_rename_v = /*...*/;

需要 C++17。

对于值列表 `L`,`mp_rename_v, Y>` 是 `Y`。

对于类型列表 `L`,`mp_rename_v, Y>` 是 `Y` 的别名。

代码示例 37. 使用 mp_rename_v 将类型列表转换为值列表
using L1 = mp_list<mp_false, mp_int<7>>;
using R1 = mp_rename_v<L1, mp_list_v>; // mp_list_v<false, 7>;

mp_append<L…​>

template<class... L> using mp_append = /*...*/;

`mp_append` 将 `L...` 中的列表连接成一个单一列表,其类型与第一个列表相同。`mp_append<>` 是 `mp_list<>` 的别名。`mp_append, L2, ..., Ln>` 是 `L1` 的别名。

在 C++17 下支持值列表,但在同一个 `mp_append` 中混合类型列表和值列表不支持。

代码示例 38. 将 mp_append 与各种类型的列表一起使用
using L1 = std::tuple<double, long double>;
using L2 = mp_list<int>;
using L3 = std::pair<short, long>;
using L4 = mp_list<>;

using R1 = mp_append<L1, L2, L3, L4>;
  // std::tuple<double, long double, int, short, long>
代码示例 39. 将 mp_append 与值列表一起使用
using L1 = mp_list_v<true, false>;
using L2 = mp_list_v<0, 1, 2, 3>;

using R1 = mp_append<L1, L2>; // mp_list_v<true, false, 0, 1, 2, 3>

mp_replace_front<L, T>

template<class L, class T> using mp_replace_front = /*...*/;

`mp_replace_front` 将列表 `L` 的第一个元素替换为 `T`。也就是说,`mp_replace_front, T>` 是 `L` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,`mp_replace_front, T>` 是 `L`。

代码示例 40. 将 mp_replace_front 与 std::pair 一起使用
using L1 = std::pair<int, float>;
using R1 = mp_replace_front<L1, void>; // std::pair<void, float>
代码示例 41. 将 mp_replace_front 与 std::tuple 一起使用
using L2 = std::tuple<float, double, long double>;
using R2 = mp_replace_front<L2, void>; // std::tuple<void, double, long double>
代码示例 42. 将 mp_replace_front 与 mp_list 一起使用
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_replace_front<L3, void>; // mp_list<void, char[2], char[3], char[4]>;
代码示例 43. 将 mp_replace_front 与 mp_list_v 一起使用
using L4 = mp_list_v<1, 2, 3, 4>;
using R4 = mp_replace_front<L4, mp_false>; // mp_list_v<false, 2, 3, 4>;

mp_replace_first<L, T>

template<class L, class T> using mp_replace_first = mp_replace_front<L, T>;

`mp_replace_first` 是 `mp_replace_front` 的另一个名称。

mp_replace_second<L, T>

template<class L, class T> using mp_replace_second = /*...*/;

`mp_replace_second` 将列表 `L` 的第二个元素替换为 `T`。也就是说,`mp_replace_second, T>` 是 `L` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,`mp_replace_second, T>` 是 `L`。

代码示例 44. 将 mp_replace_second 与 std::pair 一起使用
using L1 = std::pair<int, float>;
using R1 = mp_replace_second<L1, void>; // std::pair<int, void>
代码示例 45. 将 mp_replace_second 与 std::tuple 一起使用
using L2 = std::tuple<float, double, long double>;
using R2 = mp_replace_second<L2, void>; // std::tuple<float, void, long double>
代码示例 46. 将 mp_replace_second 与 mp_list 一起使用
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_replace_second<L3, void>; // mp_list<char[1], void, char[3], char[4]>;
代码示例 47. 将 mp_replace_second 与 mp_list_v 一起使用
using L4 = mp_list_v<1, 2, 3, 4>;
using R4 = mp_replace_second<L4, mp_false>; // mp_list_v<1, false, 3, 4>;

mp_replace_third<L, T>

template<class L, class T> using mp_replace_third = /*...*/;

`mp_replace_third` 将列表 `L` 的第三个元素替换为 `T`。也就是说,`mp_replace_third, T>` 是 `L` 的别名。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,`mp_replace_third, T>` 是 `L`。

代码示例 48. 将 mp_replace_third 与 std::tuple 一起使用
using L1 = std::tuple<float, double, long double>;
using R1 = mp_replace_third<L1, void>; // std::tuple<float, double, void>
代码示例 49. 将 mp_replace_third 与 mp_list 一起使用
using L2 = mp_list<char[1], char[2], char[3], char[4]>;
using R2 = mp_replace_third<L2, void>; // mp_list<char[1], char[2], void, char[4]>;
代码示例 50. 将 mp_replace_third 与 mp_list_v 一起使用
using L4 = mp_list_v<1, 2, 3, 4>;
using R4 = mp_replace_third<L4, mp_false>; // mp_list_v<1, 2, false, 4>;

mp_transform_front<L, F>

template<class L, template<class...> class F> using mp_transform_front =
    /*...*/;

`mp_transform_front` 将列表 `L` 的第一个元素 `T1` 替换为 `F`。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,替换是 `F>::value`。

mp_transform_front_q<L, Q>

template<class L, class Q> using mp_transform_front_q =
    mp_transform_front<L, Q::template fn>;

同 `mp_transform_front`,但接受一个带引号的元函数。

mp_transform_first<L, F>

template<class L, template<class...> class F> using mp_transform_first =
    mp_transform_front<L, F>;

`mp_transform_first` 是 `mp_transform_front` 的另一个名称。

mp_transform_first_q<L, Q>

template<class L, class Q> using mp_transform_first_q =
    mp_transform_first<L, Q::template fn>;

同 `mp_transform_first`,但接受一个带引号的元函数。

mp_transform_second<L, F>

template<class L, template<class...> class F> using mp_transform_second =
    /*...*/;

`mp_transform_second` 将列表 `L` 的第二个元素 `T2` 替换为 `F`。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,替换是 `F>::value`。

mp_transform_second_q<L, Q>

template<class L, class Q> using mp_transform_second_q =
    mp_transform_second<L, Q::template fn>;

同 `mp_transform_second`,但接受一个带引号的元函数。

mp_transform_third<L, F>

template<class L, template<class...> class F> using mp_transform_third =
    /*...*/;

`mp_transform_third` 将列表 `L` 的第三个元素 `T3` 替换为 `F`。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,替换是 `F>::value`。

mp_transform_third_q<L, Q>

template<class L, class Q> using mp_transform_third_q =
    mp_transform_third<L, Q::template fn>;

同 `mp_transform_third`,但接受一个带引号的元函数。

实用组件, <boost/mp11/utility.hpp>

mp_identity<T>

template<class T> struct mp_identity
{
    using type = T;
};

`mp_identity` 是一个简单的 转换类型特性(根据 C++ 标准),它只是返回相同的类型。它既可作为类型特性使用,也可作为类型包装器将类型作为值传递给函数。

代码示例 51. 将 mp_identity 作为类型特性使用
template<class T> using addp_if_not_ref =
    typename mp_if<std::is_reference<T>, mp_identity<T>, std::add_pointer<T>>::type;
代码示例 52. 使用 mp_identity 保护限定符和引用
template<class T> void print1()
{
    std::cout << typeid(T).name() << std::endl;
}

template<class T> void print2()
{
    std::cout << typeid(mp_identity<T>).name() << std::endl;
}

int main()
{
    print1<int const&>(); // 'int'
    print2<int const&>(); // 'mp_identity<int const &>'
}

mp_identity_t<T>

template<class T> using mp_identity_t = typename mp_identity<T>::type;

mp_inherit<T…​>

template<class... T> struct mp_inherit: T... {};

mp_if_c<C, T, E…​>

template<bool C, class T, class... E> using mp_if_c = /*...*/;

`mp_if_c` 是 `T` 的别名。`mp_if_c` 是 `E` 的别名。否则,结果是替换失败。

代码示例 53. 使用 mp_if_c 在两个替代方案之间进行选择
using R1 = mp_if_c<true, int, void>;  // int

using R2 = mp_if_c<false, int, void>; // void
代码示例 54. 在不满足条件时使用 mp_if_c 导致替换失败
template<class I> using void_if_5 = mp_if_c<I::value == 5, void>;

此示例在 `I::value` 为 5 时返回 `void`,否则生成替换失败。它与 C++14 中的 `std::enable_if_t` 或 C++11 中的 `typename std::enable_if::type` 相同。

mp_if<C, T, E…​>

template<class C, class T, class... E> using mp_if =
    mp_if_c<static_cast<bool>(C::value), T, E...>;

与 `mp_if_c` 类似,但第一个参数是类型。

代码示例 55. 使用 mp_if 在两个替代方案之间进行选择
using R1 = mp_if<mp_true, int, void>;  // int

using R2 = mp_if<mp_false, int, void>; // void
代码示例 56. 在不满足条件时使用 mp_if 导致替换失败
template<class T> using void_if_const = mp_if<std::is_const<T>, void>;

template<class... T> using void_if_all_const =
    mp_if<mp_all<std::is_const<T>...>, void>;

template<class T> using if_non_const = mp_if<mp_not<std::is_const<T>>, T>;

mp_eval_if_c<C, T, F, U…​>

template<bool C, class T, template<class...> class F, class... U> using mp_eval_if_c =
    /*...*/;

`mp_eval_if_c` 是 `T` 的别名,当 `C` 为 `true` 时;否则为 `F`。其目的是避免在条件为 `true` 时评估 `F`,因为它在这种情况下可能无效。

代码示例 57. 使用 mp_eval_if_c 选择第一个包元素,或 void
template<class... T> using first_or_void =
    mp_eval_if_c<sizeof...(T) == 0, void, mp_first, mp_list<T...>>;

mp_eval_if<C, T, F, U…​>

template<class C, class T, template<class...> class F, class... U> using mp_eval_if =
    mp_eval_if_c<static_cast<bool>(C::value), T, F, U...>;

与 `mp_eval_if_c` 类似,但第一个参数是类型。

代码示例 58. 使用 mp_eval_if 选择第一个列表元素,或 void
template<class L> using first_or_void = mp_eval_if<mp_empty<L>, void, mp_first, L>;

mp_eval_if_q<C, T, Q, U…​>

template<class C, class T, class Q, class... U> using mp_eval_if_q =
    mp_eval_if<C, T, Q::template fn, U...>;

与 `mp_eval_if` 类似,但接受一个带引号的元函数。

mp_eval_if_not<C, T, F, U…​>

template<class C, class T, template<class...> class F, class... U>
    using mp_eval_if_not = mp_eval_if<mp_not<C>, T, F, U...>;

与 `mp_eval_if` 相同,但条件相反。

mp_eval_if_not_q<C, T, Q, U…​>

template<class C, class T, class Q, class... U> using mp_eval_if_not_q =
    mp_eval_if_not<C, T, Q::template fn, U...>;

与 `mp_eval_if_not` 相同,但接受一个带引号的元函数。

mp_valid<F, T…​>

template<template<class...> class F, class... T> using mp_valid = /*...*/;

当 `F` 是一个有效的表达式时,`mp_valid` 是 `mp_true` 的别名,否则是 `mp_false` 的别名。

代码示例 59. 使用 mp_valid 编写检查嵌套类型是否存在的元函数
template<class T> using get_nested_type = typename T::type;

template<class T> struct has_nested_type: mp_valid<get_nested_type, T> {};

mp_valid_q<Q, T…​>

template<class Q, class... T> using mp_valid_q = mp_valid<Q::template fn, T...>;

与 `mp_valid` 类似,但接受一个带引号的元函数。

mp_eval_or<T, F, U…​>

template<class T, template<class...> class F, class... U> using mp_eval_or =
    mp_eval_if_not<mp_valid<F, U...>, T, F, U...>;

当表达式有效时,`mp_eval_or` 是 `F` 的别名,否则是 `T` 的别名。

代码示例 60. 使用 mp_eval_or 选择第一个包元素,或 void
template<class... T> using first_or_void =
    mp_eval_or<void, mp_first, mp_list<T...>>;

mp_eval_or_q<T, Q, U…​>

template<class T, class Q, class... U> using mp_eval_or_q =
    mp_eval_or<T, Q::template fn, U...>;

与 `mp_eval_or` 类似,但接受一个带引号的元函数。

mp_valid_and_true<F, T…​>

template<template<class...> class F, class... T> using mp_valid_and_true =
    mp_eval_or<mp_false, F, T...>;

当表达式有效时,`mp_valid_and_true` 是 `F` 的别名,否则是 `mp_false` 的别名。

mp_valid_and_true_q<Q, T…​>

template<class Q, class... T> using mp_valid_and_true_q =
    mp_valid_and_true<Q::template fn, T...>;

与 `mp_valid_and_true` 类似,但接受一个带引号的元函数。

mp_cond<C, T, R…​>

template<class C, class T, class... R> using mp_cond = /*...*/;

当 `static_cast(C::value)` 为 `true` 时,`mp_cond` 是 `T` 的别名。当 `static_cast(C::value)` 为 `false` 时,它是 `mp_cond` 的别名。

(如果 `static_cast(C::value)` 是一个替换失败,结果也是一个替换失败。)

代码示例 61. 使用 mp_cond
template<int N> using unsigned_ = mp_cond<
    mp_bool<N ==  8>, uint8_t,
    mp_bool<N == 16>, uint16_t,
    mp_bool<N == 32>, uint32_t,
    mp_bool<N == 64>, uint64_t,
    mp_true, unsigned // default case
>;

mp_defer<F, T…​>

template<template<class...> class F, class... T> using mp_defer = /*...*/;

当 `mp_valid` 为 `mp_true` 时,`mp_defer` 是一个带有嵌套类型 `type` 的结构体,该类型是 `F` 的别名。否则,`mp_defer` 是一个空结构体。

mp_quote<F>

template<template<class...> class F> struct mp_quote
{
    template<class... T> using fn = F<T...>;
};

`mp_quote` 将模板 `F` 转换为一个 引用元函数,一个带有嵌套模板 `fn` 的类型,使得 `fn` 返回 `F`。

代码示例 62. 使用 mp_quote 创建元函数列表
using LQ = mp_list<mp_quote<std::is_const>, mp_quote<std::is_volatile>>;

mp_quote_trait<F>

template<template<class...> class F> struct mp_quote_trait
{
    template<class... T> using fn = typename F<T...>::type;
};

`mp_quote_trait` 将 C++03 风格的 trait `F` 转换为一个带引号的元函数。

代码示例 63. 将 mp_quote_trait 与 std::add_pointer 一起使用
using L1 = mp_list<int, void, float>;
using R1 = mp_transform_q<mp_quote_trait<std::add_pointer>, L1>;
  // mp_list<int*, void*, float*>

mp_invoke_q<Q, T…​>

template<class Q, class... T> using mp_invoke_q = typename Q::template fn<T...>;

`mp_invoke_q` 评估带引号元函数的嵌套模板 `fn`。`mp_invoke_q, T...>` 返回 `F`。

代码示例 64. 使用 mp_invoke_q 调用元函数列表,方法 1
using LQ = mp_list<mp_quote<std::is_const>, mp_quote<std::is_volatile>>;

template<class T> using is_const_and_volatile =
    mp_apply<mp_all, mp_product<mp_invoke_q, LQ, mp_list<T>>>;
代码示例 65. 使用 mp_invoke_q 调用元函数列表,方法 2
template<class T> using is_const_and_volatile =
    mp_apply<mp_all, mp_transform_q<mp_bind_back<mp_invoke_q, T>, LQ>>;
代码示例 66. 使用 mp_invoke_q 调用元函数列表,方法 3
template<class T> using is_const_and_volatile =
    mp_apply<mp_all, mp_transform<mp_invoke_q, LQ, mp_fill<LQ, T>>>;

mp_not_fn<P>

template<template<class...> class P> struct mp_not_fn
{
    template<class... T> using fn = mp_not<P<T...>>;
};

`mp_not_fn

` 返回一个带引号的元函数 `Q`,使得 `Q::fn` 返回 `mp_not>`。

也就是说,它否定了 `P` 的结果。

mp_not_fn_q<Q>

template<class Q> using mp_not_fn_q = mp_not_fn<Q::template fn>;

同 `mp_not_fn`,但接受一个带引号的元函数。

mp_compose<F…​>

template<template<class...> class... F> struct mp_compose;

`mp_compose` 是一个带引号的元函数,它依次将其参数应用于 `F1`、`F2`、...、`Fn`。也就是说,`mp_compose::fn` 是 `Fn<...F2>...>`。

mp_compose_q<Q…​>

template<class... Q> struct mp_compose_q;

与 `mp_compose` 相同,但接受带引号的元函数。

算法, <boost/mp11/algorithm.hpp>

mp_transform<F, L…​>

template<template<class...> class F, class... L> using mp_transform = /*...*/;

`mp_transform, L2, ..., Ln>` 将 `F` 应用于每个连续的元素元组,并返回 `L1...>`。

在 C++17 下对值列表 `L...` 有有限的支持(用于一到三个列表)。在这种情况下,元素在传递给 `F` 之前用 `mp_value` 包装,之后解包。结果是 `L1, mp_value, ...>::value...>`。

代码示例 67. 使用 mp_transform 从指针列表生成指向类型列表
template<class T> using add_pointer_t =
    typename std::add_pointer<T>::type;  // std::add_pointer_t in C++14

using L1 = std::tuple<void, int, float>;
using R1 = mp_transform<add_pointer_t, L1>; // std::tuple<void*, int*, float*>
代码示例 68. 使用 mp_transform 比较两个类型列表的内容
using L1 = std::tuple<void, int, float>;
using L2 = mp_list<void, int, float>;

using R1 = mp_apply<mp_all, mp_transform<std::is_same, L1, L2>>; // mp_true
代码示例 69. 使用 mp_transform 比较两个整数常量列表的内容
template<class T1, class T2> using eq = mp_bool<T1::value == T2::value>;

using L1 = std::tuple<mp_int<1>, mp_int<2>, mp_int<3>>;
using L2 = mp_list<mp_size_t<1>, mp_size_t<2>, mp_size_t<3>>;

using R1 = mp_apply<mp_all, mp_transform<eq, L1, L2>>; // mp_true
图 1. 单个列表上的 mp_transform

L1

A1

A2

...​

An

mp_transform<F, L1>

F<A1>

F<A2>

...​

F<An>

图 2. 两个列表上的 mp_transform

L1

A1

A2

...​

An

L2

B1

B2

...​

Bn

mp_transform<F, L1, L2>

F<A1,B1>

F<A2,B2>

...​

F<An,Bn>

mp_transform_q<Q, L…​>

template<class Q, class... L> using mp_transform_q =
    mp_transform<Q::template fn, L...>;

与 `mp_transform` 相同,但接受一个带引号的元函数。

代码示例 70. 使用 mp_transform_q 计算列表中 `void` 的出现次数
using L1 = std::tuple<void, int, float, void, int>;

using R1 = mp_apply<mp_plus,
    mp_transform_q<mp_bind_front<std::is_same, void>, L1>>; // mp_int<2>
图 3. 两个列表上的 mp_transform_q

L1

A1

A2

...​

An

L2

B1

B2

...​

Bn

mp_transform_q<Q, L1, L2>

Q::fn<A1,B1>

Q::fn<A2,B2>

...​

Q::fn<An,Bn>

mp_transform_if<P, F, L…​>

template<template<class...> class P, template<class...> class F, class... L>
    using mp_transform_if = /*...*/;

`mp_transform_if` 将列表 `L1` 中满足 `mp_to_bool>` 为 `mp_true` 的元素替换为 `F`,并返回结果,其中 `Ti` 是 `Li` 的对应元素。

代码示例 71. 使用 mp_transform_if 将列表中 `void` 的出现替换为第二个列表的相应元素
using L1 = std::tuple<void, int, float, void, int>;
using L2 = std::tuple<char[1], char[2], char[3], char[4], char[5]>;

template<class T1, class T2> using first_is_void = std::is_same<T1, void>;
template<class T1, class T2> using second = T2;

using R1 = mp_transform_if<first_is_void, second, L1, L2>;
  // std::tuple<char[1], int, float, char[4], int>
插图 4. mp_transform_if

L1

A1

A2

...​

An

L2

B1

B2

...​

Bn

P<Ai, Bi>

mp_false

mp_true

...​

mp_false

mp_transform_if<P, F, L1, L2>

A1

F<A2,B2>

...​

An

mp_transform_if_q<Qp, Qf, L…​>

template<class Qp, class Qf, class... L> using mp_transform_if_q =
    mp_transform_if<Qp::template fn, Qf::template fn, L...>;

与 `mp_transform_if` 相同,但接受带引号的元函数。

代码示例 72. 使用 mp_transform_if_q 将列表中 'void' 的出现替换为第二个列表的相应元素
using L1 = std::tuple<void, int, float, void, int>;
using L2 = std::tuple<char[1], char[2], char[3], char[4], char[5]>;

using R1 = mp_transform_if_q<mp_bind<std::is_same, _1, void>, _2, L1, L2>;
  // std::tuple<char[1], int, float, char[4], int>
插图 5. mp_transform_if_q

L1

A1

A2

...​

An

L2

B1

B2

...​

Bn

Qp::fn<Ai, Bi>

mp_false

mp_true

...​

mp_false

mp_transform_if_q<Qp, _2, L1, L2>

A1

B2

...​

An

mp_filter<P, L…​>

template<template<class...> class P, class... L> using mp_filter = /*...*/;

`mp_filter` 移除列表 `L1` 中满足 `mp_to_bool>` 为 `mp_false` 的元素,并返回结果,其中 `Ti` 是 `Li` 的对应元素。

另请参阅 `mp_copy_if` 和 `mp_remove_if`,它们是 `mp_filter` 的不太通用的变体,只接受一个列表。

mp_filter_q<Qp, L…​>

template<class Qp, class... L> using mp_filter_q =
    mp_filter<Qp::template fn, L...>;

与 `mp_filter` 相同,但接受一个带引号的元函数。

代码示例 73. 使用 mp_filter_q 根据另一个列表中的掩码选择列表元素
using L1 = std::tuple<void, int, float>;
using L2 = mp_list<mp_true, mp_false, mp_true>;
using R1 = mp_filter_q<_2, L1, L2>; // std::tuple<void, float>

mp_fill<L, V>

template<class L, class V> using mp_fill = /*...*/;

`mp_fill, V>` 返回 `L`,结果的大小与输入相同。

在 C++17 下支持将值列表作为 `L`。在这种情况下,元素会被 `V::value` 替换。

代码示例 74. 将 mp_fill 与 std::tuple 一起使用
using L1 = std::tuple<void, int, float>;
using R1 = mp_fill<L1, double>; // std::tuple<double, double, double>
代码示例 75. 将 mp_fill 与 std::pair 一起使用
using L1 = std::pair<int, float>;
using R1 = mp_fill<L1, void>; // std::pair<void, void>
代码示例 76. 将 mp_fill 与 mp_list_v 一起使用
using L1 = mp_list_v<true, false>;
using R1 = mp_fill<L1, mp_int<7>>; // mp_list_v<7, 7>
图 6. mp_fill

L1

A1

A2

...​

An

mp_fill<L1, V>

V

V

...​

V

mp_count<L, V>

template<class L, class V> using mp_count = /*...*/;

`mp_count` 返回 `mp_size_t`,其中 `N` 是 `L` 中与 `V` 相同的元素数量。

mp_count_if<L, P>

template<class L, template<class...> class P> using mp_count_if = /*...*/;

`mp_count_if` 返回 `mp_size_t`,其中 `N` 是 `L` 中元素 `T` 的数量,对于这些元素 `mp_to_bool>` 为 `mp_true`。

mp_count_if_q<L, Q>

template<class L, class Q> using mp_count_if_q = mp_count_if<L, Q::template fn>;

与 `mp_count_if` 相同,但接受一个带引号的元函数。

mp_contains<L, V>

template<class L, class V> using mp_contains = mp_to_bool<mp_count<L, V>>;

当 `L` 包含元素 `V` 时,`mp_contains` 为 `mp_true`,否则为 `mp_false`。

mp_starts_with<L1, L2>

template<class L1, class L2> using mp_starts_with = /*...*/;

当 `L1` 以 `L2` 开头时,`mp_starts_with` 为 `mp_true`,否则为 `mp_false`。

mp_repeat_c<L, N>

template<class L, std::size_t N> using mp_repeat_c = /*...*/;

`mp_repeat_c` 返回一个与 `L` 形式相同的列表,其中包含 `N` 个 `L` 的连接副本。

在 C++17 下,支持将值列表作为 `L`。

代码示例 77. 使用 mp_repeat_c
using L1 = tuple<int>;
using R1 = mp_repeat_c<L1, 3>; // tuple<int, int, int>

using L2 = pair<int, float>;
using R2 = mp_repeat_c<L2, 1>; // pair<int, float>

using L3 = mp_list<int, float>;
using R3 = mp_repeat_c<L3, 2>; // mp_list<int, float, int, float>

using L4 = mp_list<int, float, double>;
using R4 = mp_repeat_c<L4, 0>; // mp_list<>

using L5 = mp_list_v<true, 8>;
using R5 = mp_repeat_c<L5, 2>; // mp_list_v<true, 8, true, 8>

mp_repeat<L, N>

template<class L, class N> using mp_repeat = /*...*/;

与 `mp_repeat_c` 相同,但带有类型参数 `N`。副本的数量为 `N::value`,并且必须是非负数。

在 C++17 下,支持将值列表作为 `L`。

mp_product<F, L…​>

template<template<class...> class F, class... L> using mp_product = /*...*/;

`mp_product, L2, ..., Ln>` 评估 `F`,其中 `Ui` 取自列表的笛卡尔积,就像元素 `Ui` 由 `n` 个嵌套循环形成,每个循环遍历 `Li`。它返回一个形式为 `L1` 的列表,其中包含 `F` 应用的结果。零个列表的退化情况,`mp_product`,返回 `mp_list>`。

图 7. 两个列表上的 mp_product

L1

A1

A2

...​

An

L2

B1

B2

...​

Bm

mp_product<F, L1, L2>

F<A1,B1>

F<A1,B2>

...​

F<A1,Bm>

F<A2,B1>

F<A2,B2>

...​

F<A2,Bm>

...​

F<An,B1>

F<An,B2>

...​

F<An,Bm>

mp_product_q<Q, L…​>

template<class Q, class... L> using mp_product_q = mp_product<Q::template fn, L...>;

与 `mp_product` 相同,但接受一个带引号的元函数。

mp_power_set<L>

template<class L> using mp_power_set = /*...*/;

`mp_power_set` 返回一个与 `L` 形式相同的列表,其中包含 `L` 的所有 2n 个可能子集(其中 `n` 是 `L` 的长度)。

`mp_power_set>` 返回 `L>`。

`mp_power_set>` 返回 `L>, L>`。

`mp_power_set>` 返回 `L>, L, L, L>`。

`mp_power_set>` 返回 `mp_power_set>` 和相同列表的连接,并将 `T1` 添加到每个元素的前面。

mp_drop_c<L, N>

template<class L, std::size_t N> using mp_drop_c = /*...*/;

`mp_drop_c` 删除 `L` 的前 `N` 个元素并返回结果。

在 C++17 下,支持将值列表作为 `L`。

插图 8. mp_drop_c

L1

A1

...​

Am

Am+1

...​

An

mp_drop_c<L1, M>

Am+1

...​

An

mp_drop<L, N>

template<class L, class N> using mp_drop = /*...*/;

与 `mp_drop_c` 相同,但带有类型参数 `N`。`N::value` 必须是非负数。

mp_from_sequence<S, F>

template<class S, class F = mp_int<0>> using mp_from_sequence = /*...*/

`mp_from_sequence` 将 `make_integer_sequence` 生成的整数序列转换为相应 `std::integral_constant` 类型的 `mp_list`。如果提供了可选的第二个参数 `F`,则所有值都将加上 `F::value` 的偏移量。

给定

template<class T, T... I> struct S;

`mp_from_sequence, F>` 是 `mp_list...>` 的别名。

mp_iota_c<N, F>

template<std::size_t N, std::size_t F = 0> using mp_iota_c = /*...*/;

`mp_iota_c` 是 `mp_list, mp_size_t, ..., mp_size_t>` 的别名。

插图 9. mp_iota_c

mp_iota_c<4>

mp_size_t<0>

mp_size_t<1>

mp_size_t<2>

mp_size_t<3>

mp_iota_c<4, 2>

mp_size_t<2>

mp_size_t<3>

mp_size_t<4>

mp_size_t<5>

mp_iota<N, F>

template<class N, class F = mp_int<0>> using mp_iota = /*...*/;

与 `mp_iota_c` 相同,但带有类型参数 `N` 和 `F`。`N::value` 必须是非负数。返回 `mp_list, std::integral_constant, ..., std::integral_constant>`,其中 `T` 是 `N::value` 的类型。

插图 10. mp_iota

mp_iota<mp_size_t<4>>

mp_size_t<0>

mp_size_t<1>

mp_size_t<2>

mp_size_t<3>

mp_iota<mp_int<4>, mp_int<-2>>

mp_int<-2>

mp_int<-1>

mp_int<0>

mp_int<+1>

mp_at_c<L, I>

template<class L, std::size_t I> using mp_at_c = /*...*/;

`mp_at_c` 返回 `L` 的第 `I` 个元素,从零开始计数。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,元素将用 `mp_value` 包装后返回。

mp_at<L, I>

template<class L, class I> using mp_at = /*...*/;

与 `mp_at_c` 相同,但带有类型参数 `I`。`I::value` 必须是非负数。

mp_take_c<L, N>

template<class L, std::size_t N> using mp_take_c = /*...*/;

`mp_take_c` 返回一个与 `L` 形式相同的列表,其中包含 `L` 的前 `N` 个元素。

在 C++17 下,支持将值列表作为 `L`。

插图 11. mp_take_c

L1

A1

...​

Am

Am+1

...​

An

mp_take_c<L1, M>

A1

...​

Am

mp_take<L, N>

template<class L, class N> using mp_take = /*...*/;

与 `mp_take_c` 相同,但带有类型参数 `N`。`N::value` 必须是非负数。

mp_slice_c<L, I, J>

template<class L, std::size_t I, std::size_t J> using mp_slice_c = mp_drop_c<mp_take_c<L, J>, I>;

`mp_slice_c` 返回一个与 `L` 形式相同的列表,其中包含从索引 `I`(包含)到索引 `J`(不包含)的元素。

在 C++17 下,支持将值列表作为 `L`。

插图 12. mp_slice_c

L1

A0

...​

Ai

...​

Aj-1

Aj

...​

An-1

mp_slice_c<L1, I, J>

Ai

...​

Aj-1

mp_slice<L, I, J>

template<class L, class I, class J> using mp_slice = mp_drop<mp_take<L, J>, I>;

与 `mp_slice_c` 相同,但带有类型参数 `I` 和 `J`。`I::value` 和 `J::value` 必须是非负数。

mp_back<L>

template<class L> using mp_back = mp_at_c<L, mp_size<L>::value - 1>;

`mp_back` 返回列表 `L` 的最后一个元素。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,元素将用 `mp_value` 包装后返回。

mp_pop_back<L>

template<class L> using mp_pop_back = mp_take_c<L, mp_size<L>::value - 1>;

`mp_pop_back` 移除列表 `L` 的最后一个元素并返回结果。

在 C++17 下,支持将值列表作为 `L`。

mp_insert_c<L, I, T…​>

template<class L, std::size_t I, class... T> using mp_insert_c =
    mp_append<mp_take_c<L, I>, mp_push_front<mp_drop_c<L, I>, T...>>;

将元素 `T...` 插入列表 `L` 的位置 `I`(从零开始的索引)。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,将插入元素 `T::value...`。

插图 13. 带有两个元素的 mp_insert_c

L1

A1

...​

Am

Am+1

...​

An

mp_insert_c<L1, M, B1, B2>

A1

...​

Am

B1

B2

Am+1

...​

An

mp_insert<L, I, T…​>

template<class L, class I, class... T> using mp_insert =
    mp_append<mp_take<L, I>, mp_push_front<mp_drop<L, I>, T...>>;

与 `mp_insert_c` 相同,但带有类型参数 `I`。

mp_erase_c<L, I, J>

template<class L, std::size_t I, std::size_t J> using mp_erase_c =
    mp_append<mp_take_c<L, I>, mp_drop_c<L, J>>;

从列表 `L` 中移除索引从 `I`(包含)到 `J`(不包含)的元素。

在 C++17 下,支持将值列表作为 `L`。

插图 14. mp_erase_c

L1

A0

...​

Ai-1

Ai

...​

Aj-1

Aj

...​

An-1

mp_erase_c<L1, I, J>

A0

...​

Ai-1

Aj

...​

An-1

mp_erase<L, I, J>

template<class L, class I, class J> using mp_erase =
    mp_append<mp_take<L, I>, mp_drop<L, J>>;

与 `mp_erase_c` 相同,但带有类型参数 `I` 和 `J`。

mp_replace<L, V, W>

template<class L, class V, class W> using mp_replace = /*...*/;

将 `L` 的所有 `V` 元素替换为 `W` 并返回结果。

插图 15. mp_replace

L1

A1

V

...​

An

mp_replace<L1, V, W>

A1

W

...​

An

mp_replace_if<L, P, W>

template<class L, template<class...> class P, class W> using mp_replace_if = /*...*/;

将 `L` 中所有满足 `mp_to_bool>` 为 `mp_true` 的 `T` 元素替换为 `W` 并返回结果。

插图 16. mp_replace_if

L1

A1

A2

...​

An

P<Ai>

mp_false

mp_true

...​

mp_false

mp_replace_if<L1, P, W>

A1

W

...​

An

mp_replace_if_q<L, Q, W>

template<class L, class Q, class W> using mp_replace_if_q =
    mp_replace_if<L, Q::template fn, W>;

与 `mp_replace_if` 相同,但接受一个带引号的元函数。

mp_replace_at_c<L, I, W>

template<class L, std::size_t I, class W> using mp_replace_at_c = /*...*/;

将 `L` 中零基索引 `I` 处的元素替换为 `W` 并返回结果。

mp_replace_at<L, I, W>

template<class L, class I, class W> using mp_replace_at = /*...*/;

与 `mp_replace_at_c` 相同,但带有类型参数 `I`。`I::value` 必须是非负数。

mp_rotate_left_c<L, N>

template<class L, std::size_t N> using mp_rotate_left_c = /*...*/;

将列表 `L` 的前 `N % M` 个元素移动到末尾,其中 `M` 是 `L` 的大小。空列表不变。

mp_rotate_left<L, N>

template<class L, class N> using mp_rotate_left = /*...*/;

与 `mp_rotate_left_c` 相同,但带有类型参数 `N`。`N::value` 必须是非负数。

mp_rotate_right_c<L, N>

template<class L, std::size_t N> using mp_rotate_right_c = /*...*/;

将列表 `L` 的后 `N % M` 个元素移动到前面,其中 `M` 是 `L` 的大小。空列表不变。

mp_rotate_right<L, N>

template<class L, class N> using mp_rotate_right = /*...*/;

与 `mp_rotate_right_c` 相同,但带有类型参数 `N`。`N::value` 必须是非负数。

mp_copy_if<L, P>

template<class L, template<class...> class P> using mp_copy_if = /*...*/;

将 `L` 中满足 `mp_to_bool>` 为 `mp_true` 的元素 `T` 复制到一个新的相同形式的列表中并返回。

mp_copy_if_q<L, Q>

template<class L, class Q> using mp_copy_if_q = mp_copy_if<L, Q::template fn>;

与 `mp_copy_if` 相同,但接受一个带引号的元函数。

mp_remove<L, V>

template<class L, class V> using mp_remove = /*...*/;

删除 `L` 的所有 `V` 元素并返回结果。

mp_remove_if<L, P>

template<class L, template<class...> class P> using mp_remove_if = /*...*/;

删除 `L` 中所有满足 `mp_to_bool>` 为 `mp_true` 的元素 `T`,并返回结果。

mp_remove_if_q<L, Q>

template<class L, class Q> using mp_remove_if_q = mp_remove_if<L, Q::template fn>;

与 `mp_remove_if` 相同,但接受一个带引号的元函数。

mp_flatten<L>

template<class L, class L2 = mp_clear<L>> using mp_flatten = /*...*/;

将 `L` 中所有与 `L2` 形式相同的列表元素 `T`(即那些满足 `mp_similar` 为 `mp_true` 的元素)替换为它们的元素并返回结果。

代码示例 78. 使用 mp_flatten
using L1 = tuple<int, tuple<>, void, tuple<float, double>>;
using R1 = mp_flatten<L1>; // tuple<int, void, float, double>

using L2 = mp_list<int, mp_list<float>, tuple<void>>;
using R2a = mp_flatten<L2>; // mp_list<int, float, tuple<void>>
using R2b = mp_flatten<L2, tuple<>>; // mp_list<int, mp_list<float>, void>

using L3 = mp_list<mp_list<float>, mp_list<mp_list<void>>>;
using R3 = mp_flatten<L3>; // mp_list<float, mp_list<void>>

mp_intersperse<L, S>

template<class L, class S> using mp_intersperse = /*...*/;

在列表 `L` 的元素之间插入分隔符 `S`。

`mp_intersperse, S>` 是 `L<>>`。`mp_intersperse, S>` 是 `L>`。`mp_intersperse, S>` 是 `L>`。

mp_split<L, S>

template<class L, class S> using mp_split = /*...*/;

在每个分隔符 `S` 处将列表 `L` 分割成段,并返回一个段列表。

`mp_split, S>` 是 `L>>`。`mp_split, S>`,其中 `S` 不在 `T...` 中,是 `L>>`。`mp_split, S>` 是 `L>, L>, L>>`。

片段可能为空;`mp_split, S>` 是 `L>, L>, L<>>, L<>>>`。

mp_join<L, S>

template<class L, class S> using mp_join = /*...*/;

`mp_join` 是 `mp_split` 的逆操作;它接受一个片段列表 `L`,并将其连接成一个单一列表,在它们之间插入分隔符 `S`。

`mp_join, S>` 返回原始列表 `L`。

例如,`mp_split, S>` 得到 `L, L>`,而 `mp_join, L>, S>` 结果为 `L`。

`mp_join` 等价于(并实现为)`mp_apply>>`。

mp_partition<L, P>

template<class L, template<class...> class P> using mp_partition = /*...*/;

`mp_partition, P>` 将 `L` 分割成两个列表 `L` 和 `L`,使得 `mp_to_bool>` 对于 `L` 的元素为 `mp_true`,对于 `L` 的元素为 `mp_false`。返回 `L, L>`。

mp_partition_q<L, Q>

template<class L, class Q> using mp_partition_q = mp_partition<L, Q::template fn>;

与 `mp_partition` 相同,但接受一个带引号的元函数。

mp_sort<L, P>

template<class L, template<class...> class P> using mp_sort = /*...*/;

`mp_sort` 根据严格弱序 `mp_to_bool>` 对列表 `L` 进行排序。

代码示例 79. 使用 mp_sort 对 std::ratio 值列表进行排序
#include <ratio>

using L1 = mp_list<std::ratio<1,2>, std::ratio<1,4>>;
using R1 = mp_sort<L1, std::ratio_less>; // mp_list<ratio<1,4>, ratio<1,2>>

mp_sort_q<L, Q>

template<class L, class Q> using mp_sort_q = mp_sort<L, Q::template fn>;

与 `mp_sort` 相同,但接受一个带引号的元函数。

mp_nth_element_c<L, I, P>

template<class L, std::size_t I, template<class...> class P> using mp_nth_element_c =
    /*...*/;

返回 `mp_sort` 中位于位置 `I` 的元素。

mp_nth_element<L, I, P>

template<class L, class I, template<class...> class P> using mp_nth_element = /*...*/;

同 `mp_nth_element_c`,但带有类型参数 `I`。`I::value` 必须是非负数。

mp_nth_element_q<L, I, Q>

template<class L, class I, class Q> using mp_nth_element_q =
    mp_nth_element<L, I, Q::template fn>;

与 `mp_nth_element` 类似,但接受一个带引号的元函数。

mp_min_element<L, P>

template<class L, template<class...> class P> using mp_min_element = /*...*/;

`mp_min_element` 返回列表 `L` 中根据排序 `mp_to_bool>` 的最小元素。

它等价于 `mp_fold, mp_first, F>`,其中 `F` 返回 `mp_if, T, U>`。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,元素将用 `mp_value` 包装后返回。

mp_min_element_q<L, Q>

template<class L, class Q> using mp_min_element_q = mp_min_element<L, Q::template fn>;

与 `mp_min_element` 相同,但接受一个带引号的元函数。

mp_max_element<L, P>

template<class L, template<class...> class P> using mp_max_element = /*...*/;

`mp_max_element` 返回列表 `L` 中根据排序 `mp_to_bool>` 的最大元素。

它等价于 `mp_fold, mp_first, F>`,其中 `F` 返回 `mp_if, T, U>`。

在 C++17 下,支持将值列表作为 `L`。在这种情况下,元素将用 `mp_value` 包装后返回。

mp_max_element_q<L, Q>

template<class L, class Q> using mp_max_element_q = mp_max_element<L, Q::template fn>;

与 `mp_max_element` 相同,但接受一个带引号的元函数。

mp_find<L, V>

template<class L, class V> using mp_find = /*...*/;

`mp_find` 返回类型 `V` 在列表 `L` 中的索引。它是 `mp_size_t` 的别名,其中 `I` 是 `V` 在 `L` 中第一次出现的零基索引。如果 `L` 不包含 `V`,`mp_find` 是 `mp_size`。

mp_find_if<L, P>

template<class L, template<class...> class P> using mp_find_if = /*...*/;

`mp_find_f` 是 `mp_size_t` 的别名,其中 `I` 是 `L` 中第一个元素 `T` 的零基索引,对于该元素 `mp_to_bool>` 为 `mp_true`。如果不存在这样的元素,`mp_find_if` 是 `mp_size`。

mp_find_if_q<L, Q>

template<class L, class Q> using mp_find_if_q = mp_find_if<L, Q::template fn>;

与 `mp_find_if` 相同,但接受一个带引号的元函数。

mp_reverse<L>

template<class L> using mp_reverse = /*...*/;

`mp_reverse>` 是 `L>`。

插图 17. mp_reverse

L1

A1

A2

...​

An

mp_reverse<L1>

An

An-1

...​

A1

mp_fold<L, V, F>

template<class L, class V, template<class...> class F> using mp_fold = /*...*/;

`mp_fold, V, F>` 是 `F< F< F< F, T2>, ...>, Tn>`,如果 `L` 为空,则为 `V`。

代码示例 80. 使用 mp_fold 添加 std::ratio 值列表的内容
#include <ratio>

using L1 = mp_list<std::ratio<1,8>, std::ratio<1,4>, std::ratio<1,2>>;
using R1 = mp_fold<L1, std::ratio<0,1>, std::ratio_add>; // std::ratio<7,8>

mp_fold_q<L, V, Q>

template<class L, class V, class Q> using mp_fold_q =
    mp_fold<L, V, Q::template fn>;

与 `mp_fold` 相同,但接受一个带引号的元函数。

mp_reverse_fold<L, V, F>

template<class L, class V, template<class...> class F> using mp_reverse_fold =
    /*...*/;

`mp_reverse_fold, V, F>` 是 `F>>>`,如果 `L` 为空,则为 `V`。

mp_reverse_fold_q<L, V, Q>

template<class L, class V, class Q> using mp_reverse_fold_q =
    mp_reverse_fold<L, V, Q::template fn>;

与 `mp_reverse_fold` 相同,但接受一个带引号的元函数。

mp_partial_sum<L, V, F>

template<class L, class V, template<class...> class F> using mp_partial_sum = /*...*/;

`mp_partial_sum` 类似于 `mp_fold`,但它返回一个(与 `L` 形式相同的)列表,其中包含折叠的中间结果,而不是最终结果。`mp_partial_sum` 结果的最后一个元素与 `mp_fold` 的结果相同。

例如,`mp_fold, V, F>` 是 `F, X2>, X3>`,但 `mp_partial_sum, V, F>` 是 `mp_list, F, X2>, F, X2>, X3>>`。

通常 `F` 是 `mp_plus`,在这种情况下结果包含 `L` 的部分和。

代码示例 81. 使用 mp_partial_sum
using L1 = mp_list_c<int, 1, 2, 3, 4>;
using R1 = mp_partial_sum<L1, mp_int<0>, mp_plus>; // mp_list_c<int, 1, 3, 6, 10>

mp_partial_sum_q<L, V, Q>

template<class L, class V, class Q> using mp_partial_sum_q =
    mp_partial_sum<L, V, Q::template fn>;

与 `mp_partial_sum` 相同,但接受一个带引号的元函数。

mp_pairwise_fold<L, F>

template<class L, template<class...> class F> using mp_pairwise_fold = /*...*/;

`mp_pairwise_fold` 返回一个与 `L` 形式相同的列表,其元素是将二元元函数 `F` 应用于 `L` 中每对相邻元素的结果。也就是说,`mp_pairwise_fold, F>` 是 `L, F>`。

结果比原列表少一个元素。如果 `L` 只有一个元素,结果是空列表。如果 `L` 是空列表,结果也是空列表。

代码示例 82. 使用 mp_pairwise_fold
template<class L> using is_increasing = mp_all_of<
    mp_pairwise_fold<L, mp_less>, mp_to_bool>;

mp_pairwise_fold_q<L, Q>

template<class L, class Q> using mp_pairwise_fold_q =
    mp_pairwise_fold<L, Q::template fn>;

与 `mp_pairwise_fold` 相同,但接受一个带引号的元函数。

代码示例 83. 使用 mp_pairwise_fold_q
template<class L, template<class...> class P> using is_sorted =
    mp_none_of<mp_pairwise_fold_q<L, mp_bind<P, _2, _1>>, mp_to_bool>;

mp_sliding_fold<L, N, F>

template<class L, class N, template<class...> class F> using mp_sliding_fold = /*...*/;

`mp_sliding_fold` 返回一个与 `L` 形式相同的列表,其元素是将 n 元元函数 `F` 应用于 `L` 中每个相邻 n 元组的结果。也就是说,`mp_sliding_fold, mp_size_t<3>, F>` 是 `L, F>`。

结果比原列表少 `N-1` 个元素。如果 `L` 的元素少于 `N::value` 个,结果是空列表。

代码示例 84. 使用 mp_sliding_fold
template<class L, class N> using local_maximum =
    mp_sliding_fold<L, N, mp_max>;

mp_sliding_fold_q<L, N, Q>

template<class L, class N, class Q> using mp_sliding_fold_q =
    mp_sliding_fold<L, N, Q::template fn>;

与 `mp_sliding_fold` 相同,但接受一个带引号的元函数。

代码示例 85. 使用 mp_sliding_fold_q
struct average { template<class... C> using fn = mp_int<mp_plus<C...>::value / sizeof...(C)>; };

template<class L, class N> using moving_average =
    mp_sliding_fold_q<L, N, average>;

mp_iterate<V, F, R>

template<class V, template<class...> class F, template<class...> class R>
    using mp_iterate = /*...*/;

`mp_iterate` 依次将 `R` 应用于 `V`,直到无法再应用为止,生成序列 `V`、`R`、`R>`、`R>>`...

然后它返回一个 mp_list,其元素通过对上述值序列应用 F 形成。也就是说,它返回 mp_list<F<V>, F<R<V>>, F<R<R<V>>>, …​>

mp_iterate 在某种程度上是 mp_reverse_fold 的逆操作。给定

template<class T, class U> struct cons {};
struct nil {};

mp_reverse_fold<mp_list<X1, X2, X3>, nil, cons> 产生 cons<X1, cons<X2, cons<X3, nil>>>,当作为 V 传递给 mp_iterate<V, mp_first, mp_second> 时,它恢复原始的 mp_list<X1, X2, X3>

代码示例 86. 使用 mp_iterate
struct X1 {};
struct X2 {};
struct X3 {};

using L1 = mp_list<X1, X2, X3>;
using R1 = mp_iterate<L1, mp_first, mp_rest>; // L1

template<class T, class U> struct cons {};
struct nil {};

using V2 = mp_reverse_fold<L1, nil, cons>; // cons<X1, cons<X2, cons<X3, nil>>>
using R2 = mp_iterate<V2, mp_first, mp_second>; // L1

struct Y1 {};
struct Y2 { using value_type = double; using next_type = Y1; };
struct Y3 { using value_type = float; using next_type = Y2; };
struct Y4 { using value_type = int; using next_type = Y3; };

template<class T> using value_type = typename T::value_type;
template<class T> using next_type = typename T::next_type;

using R3 = mp_iterate<Y4, mp_identity_t, next_type>; // mp_list<Y4, Y3, Y2, Y1>
using R4 = mp_iterate<Y4, value_type, next_type>; // mp_list<int, float, double>

mp_iterate_q<V, Qf, Qr>

template<class V, class Qf, class Qr> using mp_iterate_q =
    mp_iterate<V, Qf::template fn, Qr::template fn>;

mp_iterate 类似,但接受带引号的元函数。

mp_unique<L>

template<class L> using mp_unique = /*...*/;

mp_unique<L> 返回一个与 L 形式相同的列表,并删除重复元素。

mp_unique_if<L, P>

template<class L, template<class...> class P> using mp_unique_if = /*...*/;

mp_unique 类似,但当 mp_to_bool<P<T, U>>mp_true 时,两个元素 TU 被视为重复。

mp_unique_if_q<L, Q>

template<class L, class Q> using mp_unique_if_q =
    mp_unique_if<L, Q::template fn>;

mp_unique_if 类似,但接受带引号的元函数。

mp_all_of<L, P>

template<class L, template<class...> class P> using mp_all_of =
    mp_bool< mp_count_if<L, P>::value == mp_size<L>::value >;

PL 的所有元素都成立时,mp_all_of<L, P>mp_true,否则为 mp_false。当 L 为空时,结果为 mp_true

mp_all_of_q<L, Q>

template<class L, class Q> using mp_all_of_q = mp_all_of<L, Q::template fn>;

mp_all_of 类似,但接受带引号的元函数。

mp_none_of<L, P>

template<class L, template<class...> class P> using mp_none_of =
    mp_bool< mp_count_if<L, P>::value == 0 >;

PL 的任何元素都不成立时,mp_none_of<L, P>mp_true,否则为 mp_false。当 L 为空时,结果为 mp_true

mp_none_of_q<L, Q>

template<class L, class Q> using mp_none_of_q = mp_none_of<L, Q::template fn>;

mp_none_of 类似,但接受带引号的元函数。

mp_any_of<L, P>

template<class L, template<class...> class P> using mp_any_of =
    mp_bool< mp_count_if<L, P>::value != 0 >;

PL 的至少一个元素成立时,mp_any_of<L, P>mp_true,否则为 mp_false。当 L 为空时,结果为 mp_false

mp_any_of_q<L, Q>

template<class L, class Q> using mp_any_of_q = mp_any_of<L, Q::template fn>;

mp_any_of 类似,但接受带引号的元函数。

mp_for_each<L>(f)

template<class L, class F> constexpr F mp_for_each(F&& f);

mp_for_each<L>(f) 按顺序为列表 L 的每个元素 T 调用 f 并传入 T()

返回 std::forward<F>(f)

代码示例 87. 使用 mp_for_each 和 C++14 lambda 打印元组
template<class... T> void print( std::tuple<T...> const & tp )
{
    std::size_t const N = sizeof...(T);

    mp_for_each<mp_iota_c<N>>( [&]( auto I ){

        // I is mp_size_t<0>, mp_size_t<1>, ..., mp_size_t<N-1>

        std::cout << std::get<I>(tp) << std::endl;

    });
}

如果列表 L 的元素不可默认构造,您可以使用 mp_for_each<mp_transform<mp_identity, L>>,它会调用 f 并传入 mp_identity<T>() 而不是 T()

mp_with_index<N>(i, f)

template<std::size_t N, class F>
  constexpr auto mp_with_index( std::size_t i, F && f )
    -> decltype(std::declval<F>()(std::declval<mp_size_t<0>>()));

mp_with_index<N>(i, f) 调用 f 并传入 mp_size_t<i>() 并返回结果。i 必须小于 N。仅在 C++14 及更高版本中为 constexpr

template<class N, class F>
  constexpr auto mp_with_index( std::size_t i, F && f )
    -> decltype(std::declval<F>()(std::declval<mp_size_t<0>>()));

返回 mp_with_index<N::value>(i, f)

代码示例 88. 使用 mp_with_index 和 C++14 lambda 打印变体的活动元素
template<class... T> void print( std::variant<T...> const& v )
{
    mp_with_index<sizeof...(T)>( v.index(), [&]( auto I ) {

        // I is mp_size_t<v.index()>{} here

        std::cout << std::get<I>( v ) << std::endl;

    });
}

集合操作, <boost/mp11/set.hpp>

集合是元素唯一的列表。

mp_is_set<S>

template<class S> using mp_is_set = /*...*/;

如果 S 是一个集合,mp_is_set<S>mp_true,否则为 mp_false

mp_set_contains<S, V>

template<class S, class V> using mp_set_contains = /*...*/;

如果类型 V 是集合 S 的一个元素,mp_set_contains<S, V>mp_true,否则为 mp_false

mp_set_push_back<S, T…​>

template<class S, class... T> using mp_set_push_back = /*...*/;

对于 T…​ 中的每个 T1,如果 T1 尚未是集合 S 的元素,mp_set_push_back<S, T…​> 会将其追加到 S 的末尾。

mp_set_push_front<S, T…​>

template<class S, class... T> using mp_set_push_front = /*...*/;

mp_set_push_front<S, T…​> 在集合 S 的前面插入那些 S 尚未包含相同类型的 T…​ 中的元素。

mp_set_union<L…​>

template<class... L> using mp_set_union = /*...*/;

mp_set_union<S, L…​> 等同于 mp_set_push_back<S, T…​>,其中 T…​ 是列表 L…​ 的组合元素。mp_set_union<>mp_list<>

mp_set_intersection<S…​>

template<class... S> using mp_set_intersection = /*...*/;

mp_set_intersection<S…​> 返回一个包含所有集合 S…​ 中都存在的元素的集合。mp_set_intersection<>mp_list<>

mp_set_difference<L, S…​>

template<class L, class... S> using mp_set_difference = /*...*/;

mp_set_difference<L, S…​> 移除列表 L 中出现在任何集合 S…​ 中的元素,并返回结果。

映射操作, <boost/mp11/map.hpp>

映射是列表的列表,内部列表至少有一个元素(键)。映射的键必须唯一。

mp_is_map<M>

template<class M> using mp_is_map = /*...*/;

如果 M 是一个映射,mp_is_map<M>mp_true,否则为 mp_false

mp_map_find<M, K>

template<class M, class K> using mp_map_find = /*...*/;

mp_map_find<M, K> 是映射 M 中键为 K 的元素的别名,如果不存在这样的元素,则为 void

mp_map_contains<M, K>

template<class M, class K> using mp_map_contains =
    mp_not<std::is_same<mp_map_find<M, K>, void>>;

如果映射 M 包含键为 K 的元素,mp_map_contains<M, K>mp_true,否则为 mp_false

mp_map_insert<M, T>

template<class M, class T> using mp_map_insert =
    mp_if< mp_map_contains<M, mp_first<T>>, M, mp_push_back<M, T> >;

如果映射 M 中不存在键为 mp_first<T> 的元素,则将元素 T 插入到映射 M 中。

mp_map_replace<M, T>

template<class M, class T> using mp_map_replace = /*...*/;

如果映射 M 不包含键为 mp_first<T> 的元素,则插入它(使用 mp_push_back<M, T>);否则,用 T 替换现有元素。

mp_map_update<M, T, F>

template<class M, class T, template<class...> class F> using mp_map_update = /*...*/;

如果映射 M 不包含键为 mp_first<T> 的元素,则插入它(使用 mp_push_back<M, T>);否则,用 L<X, F<X, Y…​>> 替换现有元素 L<X, Y…​>

代码示例 89. 使用 mp_map_update 统计列表中类型出现的次数
template<class T, class U> using inc2nd = mp_int<U::value + 1>;

template<class M, class T> using count_types =
    mp_map_update<M, std::pair<T, mp_int<1>>, inc2nd>;

using L1 = mp_list<float, char, float, float, float, float, char, float>;

using R1 = mp_fold<L1, std::tuple<>, count_types>;
// std::tuple<std::pair<float, mp_int<6>>, std::pair<char, mp_int<2>>>

mp_map_update_q<M, T, Q>

template<class M, class T, class Q> using mp_map_update_q =
    mp_map_update<M, T, Q::template fn>;

mp_map_update 类似,但接受带引号的元函数。

mp_map_erase<M, K>

template<class M, class K> using mp_map_erase = /*...*/;

如果映射 M 包含键为 K 的元素,则将其删除。

mp_map_keys<M>

template<class M> using mp_map_keys = mp_transform<mp_first, M>;

mp_map_keys<M> 返回 M 的键列表。当 M 是一个有效的映射时,键是唯一的,因此结果是一个集合。

辅助元函数, <boost/mp11/function.hpp>

mp_void<T…​>

template<class... T> using mp_void = void;

与 C++17 中的 std::void_t 相同。

mp_and<T…​>

template<class... T> using mp_and = /*...*/;

mp_and<T…​> 按顺序将 mp_to_bool 应用于 T…​ 中的类型。如果应用程序的结果是 mp_falsemp_and 返回 mp_false。如果应用程序导致替换失败,返回 mp_false。如果所有结果都是 mp_true,返回 mp_truemp_and<>mp_true

代码示例 90. mp_and 行为
using R1 = mp_and<mp_true, mp_true>;   // mp_true

using R2 = mp_and<mp_false, void>;     // mp_false, void is not reached

using R3 = mp_and<mp_false, mp_false>; // mp_false

using R4 = mp_and<void, mp_true>;      // mp_false (!)

mp_all<T…​>

template<class... T> using mp_all = /*...*/;

如果 mp_to_bool<U> 对于 T…​ 中的所有类型 U 都是 mp_true,则 mp_all<T…​>mp_true,否则为 mp_false。与 mp_and 相同,但不执行短路评估。mp_and<mp_false, void>mp_false,但 mp_all<mp_false, void> 会出错,因为 void 没有嵌套的 value。优点是 mp_all 可能更快,并且不像 mp_and 那样掩盖替换失败。

代码示例 91. mp_all 行为
using R1 = mp_all<mp_true, mp_true>;   // mp_true

using R2 = mp_all<mp_false, void>;     // compile-time error

using R3 = mp_all<mp_false, mp_false>; // mp_false

using R4 = mp_all<void, mp_true>;      // compile-time error

mp_or<T…​>

template<class... T> using mp_or = /*...*/;

mp_or<T…​> 按顺序将 mp_to_bool 应用于 T…​ 中的类型。如果应用程序的结果是 mp_truemp_or 返回 mp_true。如果所有结果都是 mp_false,返回 mp_falsemp_or<>mp_false

代码示例 92. mp_or 行为
using R1 = mp_or<mp_true, mp_false>;   // mp_true

using R2 = mp_or<mp_true, void>;       // mp_true, void is not reached

using R3 = mp_or<mp_false, mp_false>;  // mp_false

using R4 = mp_or<void, mp_true>;       // compile-time error

mp_any<T…​>

template<class... T> using mp_any = /*...*/;

如果 mp_to_bool<U> 对于 T…​ 中的任何类型 U 都是 mp_true,则 mp_any<T…​>mp_true,否则为 mp_false。与 mp_or 相同,但不执行短路评估。

代码示例 93. mp_any 行为
using R1 = mp_any<mp_true, mp_false>;  // mp_true

using R2 = mp_any<mp_true, void>;      // compile-time error

using R3 = mp_any<mp_false, mp_false>; // mp_false

using R4 = mp_any<void, mp_true>;      // compile-time error

mp_same<T…​>

template<class... T> using mp_same = /*...*/;

如果 T…​ 中的所有类型都是相同类型,则 mp_same<T…​>mp_true,否则为 mp_falsemp_same<>mp_true

mp_similar<T…​>

template<class... T> using mp_similar = /*...*/;

如果 T…​ 中的所有类型都是相同类型,或者是相同类模板的实例化,其参数都是类型,则 mp_similar<T…​>mp_true,否则为 mp_falsemp_similar<>mp_true

代码示例 94. mp_similar
using R1 = mp_similar<void>;                        // mp_true
using R2 = mp_similar<void, void>;                  // mp_true
using R3 = mp_similar<void, void, void>;            // mp_true
using R4 = mp_similar<void, void, float>;           // mp_false

template<class T> struct X;
template<class... T> struct Y;

using R5 = mp_similar<X<int>, X<void>, X<float>>;   // mp_true
using R6 = mp_similar<Y<>, Y<void>, Y<void, void>>; // mp_true
using R7 = mp_similar<X<void>, Y<void>>;            // mp_false

mp_plus<T…​>

template<class... T> using mp_plus = /*...*/;

mp_plus<T…​> 是一个整型常量类型,其值是 T…​ 中所有类型 UU::value 的总和。mp_plus<>mp_int<0>

mp_less<T1, T2>

template<class T1, class T2> using mp_less = /*...*/;

T1::value 的数值小于 T2::value 的数值时,mp_less<T1, T2>mp_true,否则为 mp_false

(请注意,这不一定与有符号类型和无符号类型进行比较时的 T1::value < T2::value 相同;-1 < 1ufalse,但 mp_less<mp_int<-1>, mp_size_t<1>>mp_true。)

mp_min<T1, T…​>

template<class T1, class... T> using mp_min = mp_min_element<mp_list<T1, T...>, mp_less>;

mp_min<T…​> 返回 T…​U::value 最低的类型 U

mp_max<T1, T…​>

template<class T1, class... T> using mp_max = mp_max_element<mp_list<T1, T...>, mp_less>;

mp_max<T…​> 返回 T…​U::value 最高的类型 U

绑定, <boost/mp11/bind.hpp>

mp_arg<I>

template<std::size_t I> struct mp_arg;

mp_arg<I> 是一个带引号的元函数,其嵌套模板 fn<T…​> 返回 T…​ 的第 I 个(从零开始)元素。

_1, …​, _9

using _1 = mp_arg<0>;
using _2 = mp_arg<1>;
using _3 = mp_arg<2>;
using _4 = mp_arg<3>;
using _5 = mp_arg<4>;
using _6 = mp_arg<5>;
using _7 = mp_arg<6>;
using _8 = mp_arg<7>;
using _9 = mp_arg<8>;

_1_9 是占位符类型,等同于 boost::bind 的占位符。

mp_bind<F, T…​>

template<template<class...> class F, class... T> struct mp_bind;

mp_bind<F, T…​> 是一个带引号的元函数,它实现了 boost::bind 的基于类型的等价物。其嵌套模板 fn<U…​> 返回 F<V…​>,其中 V…​T…​,占位符被 U…​ 的相应元素替换,并且 mp_bindmp_bind_frontmp_bind_back 表达式被替换为它们针对 U…​ 的相应评估结果。

例如,mp_bind<F, int, _2, mp_bind<G, _1>>::fn<float, void>F<int, void, G<float>>

mp_bind_q<Q, T…​>

template<class Q, class... T> using mp_bind_q = mp_bind<Q::template fn, T...>;

mp_bind 类似,但接受带引号的元函数。

mp_bind_front<F, T…​>

template<template<class...> class F, class... T> struct mp_bind_front;

mp_bind_front<F, T…​>F 的最左侧参数绑定到 T…​。其嵌套模板 fn<U…​> 返回 F<T…​, U…​>

mp_bind_front_q<Q, T…​>

template<class Q, class... T> using mp_bind_front_q =
    mp_bind_front<Q::template fn, T...>;

mp_bind_front 类似,但接受带引号的元函数。

mp_bind_back<F, T…​>

template<template<class...> class F, class... T> struct mp_bind_back;

mp_bind_back<F, T…​>F 的最右侧参数绑定到 T…​。其嵌套模板 fn<U…​> 返回 F<U…​, T…​>

mp_bind_back_q<Q, T…​>

template<class Q, class... T> using mp_bind_back_q =
    mp_bind_back<Q::template fn, T...>;

mp_bind_back 类似,但接受带引号的元函数。

Lambda 表达式, <boost/mp11/lambda.hpp>

mp_lambda<T>

template<class T> using mp_lambda = /*...*/;

mp_lambda<T> 是一个带引号的元函数,其嵌套模板 fn<U…​> 返回一个类型 V,它与 T 具有相同的语法定义,只是 T 中出现的占位符被 U…​ 的相应元素替换。

例如,mp_lambda<std::pair<_1, _2*>>::fn<int, char>std::pair<int, char*>

替换不会发生在那些由于实例化具有非类型模板参数的类模板而形成的 T 的组成部分中。

注意
在 GCC 4.8 中,一个编译器错误导致 constvolatile 限定符从返回类型 V 中被剥离(除非它们应用于函数或成员函数类型)。
注意
由于编译器限制,VS2013 及更早版本不支持 mp_lambda

整数序列, <boost/mp11/integer_sequence.hpp>

integer_sequence<T, I…​>

template<class T, T... I> struct integer_sequence
{
};

integer_sequence<T, I…​> 包含类型为 T 的整数序列。与 C++14 的 std::integer_sequence 相同。

make_integer_sequence<T, N>

template<class T, T N> using make_integer_sequence = /*...*/;

make_integer_sequence<T, N>integer_sequence<T, 0, 1, …​, N-1>。与 C++14 的 std::make_integer_sequence 相同。

index_sequence<I…​>

template<std::size_t... I> using index_sequence = integer_sequence<std::size_t, I...>;

index_sequence<I…​>integer_sequence<size_t, I…​> 的别名。与 C++14 的 std::index_sequence 相同。

make_index_sequence<N>

template<std::size_t N> using make_index_sequence =
    make_integer_sequence<std::size_t, N>;

make_index_sequence<N>index_sequence<0, 1, …​, N-1>。与 C++14 的 std::make_index_sequence 相同。

index_sequence_for<T…​>

template<class... T> using index_sequence_for =
    make_integer_sequence<std::size_t, sizeof...(T)>;

index_sequence_for<N>make_index_sequence<sizeof…​(T)>。与 C++14 的 std::index_sequence_for 相同。

元组操作, <boost/mp11/tuple.hpp>

tuple_apply(f, tp)

template<class F, class Tp> constexpr /*...*/ tuple_apply(F&& f, Tp&& tp);

tuple_apply(f, tp) 返回 std::forward<F>(f)(std::get<J>(std::forward<Tp>(tp))…​),其中 J 的范围是 0 到 N-1Nstd::tuple_size<typename std::remove_reference<Tp>::type>::value。与 C++17 中的 std::apply 相同。

construct_from_tuple<T>(tp)

template<class T, class Tp> T construct_from_tuple(Tp&& tp);

construct_from_tuple<T>(tp) 返回 T(std::get<J>(std::forward<Tp>(tp))…​),其中 J 的范围是 0 到 N-1Nstd::tuple_size<typename std::remove_reference<Tp>::type>::value。与 C++17 中的 std::make_from_tuple 相同。函数名与 C++17 的不匹配,以避免在两者都可见或在非限定调用中出现歧义。

tuple_for_each(tp, f)

template<class Tp, class F> constexpr F tuple_for_each(Tp&& tp, F&& f);

tuple_for_each(tp, f) 将函数对象 f 应用于 tp 的每个元素,通过评估表达式 f(std::get<J>(std::forward<Tp>(tp))),其中 J 的范围是 0 到 N-1Nstd::tuple_size<typename std::remove_reference<Tp>::type>::value

返回 std::forward<F>(f)

tuple_transform(f, tp…​)

template<class F, class... Tp> constexpr /*...*/ tuple_transform(F const& f, Tp&&... tp);

tuple_transform(f, tp…​) 接受一个函数对象 f,后跟一个或多个等长元组(std::tuplestd::pairstd::array 被视为元组)。

可调用对象 f 必须接受与元组数量一样多的参数。函数对象被调用时,传入每个元组的第一个元素,每个元组的第二个元素,依此类推,如同通过评估表达式 f(std::get<J>(std::forward<Tp>(tp))…​),其中 J 的范围是 0 到 N-1N 是元组的长度。

处理元组元素的顺序未指定。

结果作为 std::tuple<T…​> 返回,其中 T…​f 的返回值推导(左值引用被保留,右值引用按值返回)。

便捷头文件, <boost/mp11.hpp>

便利头文件 <boost/mp11.hpp> 包含了此参考中之前列出的所有头文件。

MPL 支持, <boost/mp11/mpl.hpp>

头文件 <boost/mp11/mpl.hpp> 在包含时定义了 mp_liststd::tuple 作为有效的 MPL 序列所需的必要支持基础结构。

注意
<boost/mp11.hpp> 不包含 mpl.hpp

还可以通过包含 <boost/mp11/mpl_list.hpp> 仅启用对 mp_list 的支持,并通过包含 <boost/mp11/mpl_tuple.hpp> 启用对 std::tuple 的支持。这可能是必需的,因为一些库(例如 Boost.Fusion)包含它们自己的对 std::tuple 的 MPL 支持,这与 Mp11 的支持冲突。

代码示例 95. 将现有 MPL 序列转换为 mp_list
using L = mpl::copy<Sequence, mpl::back_inserter<mp11::mp_list<>>>::type;

本文档

  • 版权所有 2017-2019 Peter Dimov

  • 版权所有 2017 Bjørn Reese

Glen Fernandes 慷慨地将“Simple C++11 metaprogramming”文章转换为 Asciidoc 格式,以纳入本文档。