![](/img/trans.png)
[英]How to initialize a constexpr std::array with templated constexpr member functions?
[英]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>
,所以不要要求它。 T
和K
都可以从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.