在可变参数模板参数上实现数学补码逻辑

Implementing mathematical complement logic on variadic template parameters

本文关键字:参数 补码 实现 变参      更新时间:2023-10-16

我有一个定义constexpr size_t dimensions的类。在本课程中,我实现了一个EvaluateOver<Dimensions...>(Lambda F),它在我指定的维度上执行某些操作。例如,假设dimensions=4f是某个 lambda 表达式:

MyClass.EvaluateOver<0,2>(f);

将通过执行以下扩展来执行有关 0 和 2 的f

template<size_t... Dims, typename Lambda>
inline auto EvaluateOver(const Lambda& F) const
{
F(std::get<Dims>(_memberTupleDataContainer)...);
}

现在我想要另一个成员函数,它将在未指定的维度上进行评估。因此EvaluateOverOthers<0,2>(f)将在维度 1 和 3 上执行其操作。

理想情况下,我正在考虑以下几点:

template<size_t... Dims, typename Lambda>
inline auto EvaluateOverOthers(const Lambda& F) const
{
EvaluateOver<
// variadic parameter that does the mathematical complement of
// Dims... with a variadic expansion of dimensions
>(F);
}

以下内容可能会有所帮助:

namespace details
{
template <typename Seq1, typename Seq2, typename Res = std::index_sequence<>>
struct minus;
// Nothing more to remove
template <std::size_t ... Is1, std::size_t... IRes>
struct minus<std::index_sequence<Is1...>,
std::index_sequence<>,
std::index_sequence<IRes...>>
{
using type = std::index_sequence<IRes..., Is1...>;
};
// Remove front elements as they are equal.
template <std::size_t I, std::size_t ... Is1, std::size_t ... Is2, std::size_t... IRes>
struct minus<std::index_sequence<I, Is1...>,
std::index_sequence<I, Is2...>,
std::index_sequence<IRes...>>
{
using type = typename minus<std::index_sequence<Is1...>,
std::index_sequence<Is2...>,
std::index_sequence<IRes...>>::type;
};
// Add front element to result.
template <std::size_t I1, std::size_t I2,
std::size_t ... Is1, std::size_t ... Is2,
std::size_t... IRes>
struct minus<std::index_sequence<I1, Is1...>,
std::index_sequence<I2, Is2...>,
std::index_sequence<IRes...>>
{
using type = typename minus<std::index_sequence<Is1...>,
std::index_sequence<I2, Is2...>,
std::index_sequence<IRes..., I1>>::type;
};
}
template <std::size_t N, typename Seq>
using complement = details::minus<std::make_index_sequence<N>, Seq>;
template <std::size_t N, typename Seq>
using complement_t = typename complement<N, Seq>::type;
// Some test
static_assert(std::is_same<std::index_sequence<0, 3>,
complement_t<4, std::index_sequence<1, 2>>>::value, "!");

然后

template<size_t... Is, typename Lambda>
auto EvaluateOver(const Lambda& F, std::index_sequence<Is...>) const
{
return F(std::get<Is>(_memberTupleDataContainer)...);
}
template<size_t... Dims, typename Lambda>
auto EvaluateOver(const Lambda& F) const
{
return EvaluateOver(F, std::index_sequence<Is...>{});
}
template<size_t... Is, typename Lambda>
auto EvaluateOverOthers(const Lambda& F) const
{
return EvaluateOver(F, complement_t<_dimension, std::index_sequence<Is...>>{});
}

我玩了一个constexprC++17 解决方案(在线演示)。我认为可以分解补语的逻辑(如果需要)。

#include <iostream>
#include <iterator>
#include <utility>
#include "my_constexpr_array.hpp"
template<size_t Nd>
struct MyClass {
static constexpr auto dim_arr = Array(std::make_index_sequence<Nd>{});
template<size_t... excludes, class F>
auto eval_others(F f) const {
constexpr auto excl_arr = Array{excludes...};
constexpr auto incl_pred = [&] (size_t i) { return !excl_arr.contains(i); };
constexpr auto incl_excl_arr = dim_arr.partition(incl_pred);
constexpr auto incl_count = dim_arr.count_if(incl_pred);
return eval_helper(
f,
[&] { return incl_excl_arr; },// wrapped in lambda to preserve constexpr
std::make_index_sequence<incl_count>{}// indices for trimming incl_excl_arr
);
}
template<class F, class Dims, size_t... is>
auto eval_helper(F f, Dims dims, std::index_sequence<is...>) const {
return f(std::integral_constant<size_t, dims()[is]>{}...);
}
};
int main() {
MyClass<7> foo{};
foo.eval_others<2, 4>([&] (auto... is) { (std::cout << ... << is) << "n"; });
return 0;
}

其中"my_constexpr_array.hpp"的行为类似于

template<class T, size_t size>
struct Array {
static_assert(size >= 1);
T data_[size];
constexpr Array() noexcept
: data_{} {}
template<class... Ts>
explicit constexpr Array(T v0, Ts... vs) noexcept
: data_{v0, vs...} {}
template<T... vs>
explicit constexpr Array(std::integer_sequence<T, vs...>) noexcept
: data_{vs...} {}
constexpr T* begin() { return data_; }
constexpr const T* begin() const { return data_; }
constexpr T* end() { return begin() + size; }
constexpr const T* end() const { return begin() + size; }
constexpr decltype(auto) operator[](size_t i) { return data_[i]; }
constexpr decltype(auto) operator[](size_t i) const { return data_[i]; }
constexpr bool contains(const T& v) const {
for(auto& x : *this) if(x == v) return true;
return false;
}
template<class Pred>
constexpr size_t count_if(Pred pred) const {
size_t result = 0;
for(auto& x : *this) result += size_t(pred(x) ? 1 : 0);
return result;
}
template<class Pred>
constexpr Array partition(Pred pred) const {
// return a sorted copy such that all `true`s precede all `false`s
Array result{};
T* true_false_dst[2] = {result.begin(), result.begin() + count_if(pred)};
// pair of output iterators; use first if predicate is true and vice versa
for(auto& x : *this) *true_false_dst[pred(x) ? 0 : 1]++ = x;
return result;
}
friend std::ostream& operator<<(std::ostream& os, const Array& self) {
for(auto& x : self) os << x << ", ";
return os;
}
};
template<class T, class... Ts>
Array(T, Ts...) -> Array<T, size_t(1) + sizeof...(Ts)>;
template<class T, T... vs>
Array(std::integer_sequence<T, vs...>) -> Array<T, sizeof...(vs)>;