繁体   English   中英

从“constexpr”成员方法中过滤“constexpr std::array”

[英]Filtering a `constexpr std::array` from within a `constexpr` member method

给定一个constexpr std::array ,下面的代码生成另一个constexpr std::array并删除了所有相邻的匹配值,也就是说,给定{ 1, 1, 2, 2, 3 }结果将是{ 3 }

#include <cstdint>
#include <array>

template <bool Predicate, typename True, typename False>
struct conditional;

template <typename True, typename False>
struct conditional<true, True, False> 
{
    using type = True;
};

template <typename True, typename False>
struct conditional<false, True, False> 
{
    using type = False;
};

template <bool Predicate, typename True, typename False>
using conditional_t = typename conditional<Predicate, True, False>::type;

template <typename T, T... X>
struct static_sequence 
{ 
};

template <typename T, T X, typename S>
struct static_sequence_builder;

template <typename T, T X, T... S>
struct static_sequence_builder<T, X, static_sequence<T, S...>> 
{
    using type = static_sequence<T, X, S...>;
};

template <typename T, T X, typename S>
using static_sequence_builder_t = typename static_sequence_builder<T, X, S>::type;

template <typename T, T... N>
struct remove_adjacent_matches;

template <typename T>
struct remove_adjacent_matches<T>
{
    using type = static_sequence<T>;
};

template <typename T, T Tail>
struct remove_adjacent_matches<T, Tail>
{
    using type = static_sequence<T, Tail>;
};

template <typename T, T Head_LHS, T Head_RHS, T... Tail>
struct remove_adjacent_matches<T, Head_LHS, Head_RHS, Tail...>
{
    using type = conditional_t<Head_LHS != Head_RHS, static_sequence_builder_t<T, Head_LHS, typename remove_adjacent_matches<T, Head_RHS, Tail...>::type>, typename remove_adjacent_matches<T, Tail...>::type>;
};

template <typename T, T... N>
using remove_adjacent_matches_t = typename remove_adjacent_matches<T, N...>::type;

template <typename T, T... X>
constexpr std::array<T, sizeof...(X)> static_sequence_to_array(static_sequence<T, X...>) 
{
    return std::array<T, sizeof...(X)> { X... };
}

template <typename T, uint32_t K, std::array<T, K> A, uint32_t... I>
constexpr auto identify(std::integer_sequence<uint32_t, I...> i)
{
    return static_sequence_to_array(remove_adjacent_matches_t<uint32_t, A[I]...> { });
}

template <typename T, uint32_t K, std::array<T, K> A>
constexpr auto identify()
{
    return identify<T, K, A>(std::make_integer_sequence<uint32_t, K> { });
}

int main(int argc, char **argv)
{
    static constexpr std::array<uint32_t, 7> array = { 1, 1, 1, 2, 2, 2, 3 };
    static constexpr auto identified = identify<uint32_t, 7, array>();

    static_assert(identified.size() == 3);
    static_assert(identified[0] == 1);
    static_assert(identified[1] == 2);
    static_assert(identified[2] == 3);

    return 0;
}

上面的代码提供了一个constexpr std::array作为模板参数。

但是,如果可能的话,我想实现这样一个过滤器,它可以在下面的 class (继承std::array )上作为constexpr成员方法工作。

template <uint32_t K>
struct BasisElement : public std::array<uint32_t, K>
{        
    // This most certainly does not work
    constexpr auto identify() const
    {
        return ::identify<uint32_t, K, *this>();
    }
};

int main(int argc, char **argv)
{
    static constexpr BasisElement<7> basis_element = { 1, 1, 1, 2, 2, 2, 3 };
    
    // This works
    //static constexpr auto identified = identify<uint32_t, 7, static_cast<std::array<uint32_t, 7>>(basis_element)>();

    // This does not work
    static constexpr auto identified = basis_element.identify();

    static_assert(identified.size() == 3);
    static_assert(identified[0] == 1);
    static_assert(identified[1] == 2);
    static_assert(identified[2] == 3);

    return 0;
}

目前在 C++20 中这样的事情是可能的吗? 或者至少,是否有某种解决方法可以实现类似的行为?

我认为,一般来说,当您使用Boost.Mp11时,元编程要容易得多。

该库只处理类型,所以第一步是将我们的std::array提升到类型列表中。 我们只是确保我们以一种不依赖于它作为std::array的方式编写它,然后它就可以与BasisElement正常工作。

我们可以这样做:

template <auto A>
struct index_into {
    using int_type = std::decay_t<decltype(A[0])>;

    template <typename I>
    using fn = std::integral_constant<int_type, A[I::value]>;
};

template <auto A>
using array_to_list = mp_transform_q<index_into<A>, mp_iota_c<A.size()>>;

也许有更好的方法可以做到这一点,但这是迄今为止我能想到的最好的方法。 所以在这里, array_to_list<std::array{1, 2, 3, 4}>给你类型mp_list<mp_int<1>, mp_int<2>, mp_int<3>, mp_int<4>>

这很有用的原因是 Boost.Mp11 有很多操作类型列表的算法。 所以如果你想唯一化,那就是:

template <auto A>
using uniq_elems = mp_unique<array_to_list<A>>;

如果你不想使用 Boost.Mp11(你真的应该使用,因为它很棒),那么修复你的代码主要就是删除你不需要的东西。 您目前拥有:

template <typename T, uint32_t K, std::array<T, K> A>
constexpr auto identify()
{
    return identify<T, K, A>(std::make_integer_sequence<uint32_t, K> { });
}

但是您不需要专门采用array<T, K> ,所以不要要求它。 TK都可以从A参数中确定:

template <auto A>
constexpr auto identify()
{
    using T = A::value_type;
    constexpr auto K = A.size();
    return identify<T, K, A>(std::make_integer_sequence<uint32_t, K> { });
}

这样做适用于从std::array继承的类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM