繁体   English   中英

枚举折叠表达式

[英]Enumerating over a fold expression

我有一些辅助代码,使用编译时索引执行向量重组。 生成的代码尽可能高效至关重要。 我依赖带有折叠表达式的参数包,我想知道编写这样的代码的最佳实践是什么。

一个实际的例子:让一个函数insert器将容器y的元素插入到位置为Ii容器x ,其中位置是编译时常量。 这个函数的基本签名是这样的:

template<size_t... Ii, size_t Xsize, size_t Size>
constexpr container<Xsize> insert(container<Xsize> x, container<Ysize> y);

它的调用方式如下: insert<0, 2>(x, y) 我看到实现这一点的两个明显可能性。

首先:使用辅助索引变量迭代y

template<size_t... Ii, size_t Xsize, size_t Size>
constexpr container<Xsize> insert(container<Xsize> x, container<Ysize> y) {
  int i = 0;
  ((x[Ii] = y[i++]), ...);
  return x;
}

我对这个解决方案的问题是变量i :我必须依靠编译器来优化它。

第二个解决方案避免了任何运行时依赖性,但它需要一个辅助功能,使整个实现相当丑陋:

template<size_t... Ii, size_t... Yi, size_t Xsize, size_t Size>
constexpr container<Xsize> insert_(container<Xsize> x, container<Ysize> y, std::index_sequence<Yi...>) {
  ((x[Ii] = y[Yi]), ...);
  return x;
}

template<size_t... Ii, size_t Xsize, size_t Size>
constexpr container<Xsize> insert(container<Xsize> x, container<Ysize> y) {
  return insert_<Ii...>(x,y, std::make_index_sequence<sizeof...(Ii)> {});
}

有没有办法完成这个,避免运行时变量和辅助功能?

生成的代码尽可能高效至关重要。

只是关于你的例子的旁注:你应该确保性能不会受到按值传递函数参数的影响。 返回值相同。

有没有办法完成这个,避免运行时变量和辅助功能?

您可以实现可重用的辅助函数。 例如,请考虑以下代码。

static_assert(__cplusplus >= 201703L, "example written for C++17 or later");

#include <cstddef>

#include <array>
#include <type_traits>
#include <utility>

namespace detail {

template<std::size_t... inds, class F>
constexpr void gen_inds_impl(std::index_sequence<inds...>, F&& f) {
  f(std::integral_constant<std::size_t, inds>{}...);
}

}// detail

template<std::size_t N, class F>
constexpr void gen_inds(F&& f) {
  detail::gen_inds_impl(std::make_index_sequence<N>{}, std::forward<F>(f));
}

// the code above is reusable

template<
  std::size_t... inds_out,
  class T, std::size_t size_out, std::size_t size_in
>
constexpr std::array<T, size_out> insert1(
  std::array<T, size_out> out,
  std::array<T, size_in> in
) {
  static_assert((... && (inds_out < size_out)));
  static_assert(sizeof...(inds_out) <= size_in);

  gen_inds<sizeof...(inds_out)>([&] (auto... inds_in) {
    ((out[inds_out] = in[inds_in]), ...);
  });

  return out;
}

类似的替代方法是static_for方法:

static_assert(__cplusplus >= 201703L, "example written for C++17 or later");

#include <cstddef>

#include <array>
#include <type_traits>
#include <utility>

namespace detail {

template<std::size_t... inds, class F>
constexpr void static_for_impl(std::index_sequence<inds...>, F&& f) {
  (f(std::integral_constant<std::size_t, inds>{}), ...);
}

}// detail

template<std::size_t N, class F>
constexpr void static_for(F&& f) {
  detail::static_for_impl(std::make_index_sequence<N>{}, std::forward<F>(f));
}

// the code above is reusable

template<
  std::size_t... inds_out,
  class T, std::size_t size_out, std::size_t size_in
>
constexpr std::array<T, size_out> insert2(
  std::array<T, size_out> out,
  std::array<T, size_in> in
) {
  static_assert(sizeof...(inds_out) >= 1);

  static_assert((... && (inds_out < size_out)));
  static_assert(sizeof...(inds_out) <= size_in);

  constexpr std::size_t N = sizeof...(inds_out);

  static_for<N>([&] (auto n) {
    // note the constexpr
    constexpr std::size_t ind_out = std::array{inds_out...}[n];
    constexpr std::size_t ind_in = n;
    out[ind_out] = in[ind_in];
  });

  return out;
}

我认为不可能避免运行时变量和辅助功能(希望有人可以反驳这一点)。

而且我非常喜欢你的第二个解决方案但是...如何使用y迭代器(如果y支持cbegin()和迭代器,显然)。

有点像(注意:代码没有经过测试)

template <std::size_t Ii...., std::size_t Xsize, std::size_t Ysize>
constexpr container<Xsize> insert(container<Xsize> x, container<Ysize> const & y) {
   auto it = y.cbegin();
   ((x[Ii] = *it++), ...);
   return x;
}

这几乎是你的第一个解决方案,但在访问y递增迭代器应该是(我想,对于连续的穿越,对于一些容器)更有效(一点更有效)比使用operator[]()

但我也认为,如果有一个好的优化器,就没有可察觉的差异。

暂无
暂无

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

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