Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在折叠表达式上枚举_C++_C++17_Variadic Templates_Template Meta Programming_Fold Expression - Fatal编程技术网

C++ 在折叠表达式上枚举

C++ 在折叠表达式上枚举,c++,c++17,variadic-templates,template-meta-programming,fold-expression,C++,C++17,Variadic Templates,Template Meta Programming,Fold Expression,我有一些辅助代码,可以使用编译时索引执行向量重组。最重要的是生成的代码尽可能高效。我依赖于带有折叠表达式的参数包,我想知道编写此类代码的最佳实践是什么 一个实际示例:假设有一个函数insert,它将容器y的元素插入容器x的Ii位置,其中位置是编译时常数。此函数的基本签名如下所示: template<size_t... Ii, size_t Xsize, size_t Size> constexpr container<Xsize> insert(container<

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

一个实际示例:假设有一个函数
insert
,它将容器
y
的元素插入容器
x
Ii
位置,其中位置是编译时常数。此函数的基本签名如下所示:

template<size_t... Ii, size_t Xsize, size_t Size>
constexpr container<Xsize> insert(container<Xsize> x, container<Ysize> y);
这个解决方案的问题是变量
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)> {});
}
模板
constexpr容器插入(容器x、容器y、std::index\u序列){
((x[Ii]=y[Yi]),…);
返回x;
}
模板
constexpr容器插入(容器x、容器y){
返回insert(x,y,std::make_index_sequence{});
}

有没有办法避免运行时变量和辅助函数

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

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

例如(注意:代码未测试)

模板
constexpr容器插入(容器x、容器const和y){
自动it=y.cbegin();
((x[Ii]=*it++),…);
返回x;
}
这几乎是您的第一个解决方案,但是通过增加迭代器来访问
y
应该比使用
操作符[]()
更高效(我认为,对于顺序遍历,对于某些容器而言)

但我也认为,有了一个好的优化器,没有明显的区别

最重要的是生成的代码尽可能高效

关于您的示例,请注意一点:您应该确保性能不会因为按值传递函数参数而受到影响。返回值也一样

有没有办法避免运行时变量和辅助函数

您可以实现可重用的助手函数。作为一个例子,考虑下面的代码。< /P>
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;
}

std::make_index_sequence
基本上是为这样的东西设计的。尽管它需要一个辅助函数,我还是喜欢你的第二个解决方案。如果没有另一个函数来接收索引序列,您就无法真正使用它。您有任何理由认为编译器不会优化运行时变量吗?无可否认,这很尴尬,但如果它完成了任务…?例如At-O1有。@Barry,是的,编译器非常擅长优化它。但有时它仍然不完美。问题是,这是针对SIMD处理的,而且大多数调用都应该编译成一个或两个SIMD指令。无论如何,我正在尝试找出一些可能会阻止优化程序发挥最佳效果的问题。我将研究基于labda的方法,它可能会解决我看到的其他一些设计问题。至于按值传递:所有函数都显式声明为引用透明,并将强制内联。这个领域是SIMD编程,其思想是无论如何,对这些函数的调用都会被一个或两个CPU指令所取代。
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;
}
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_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;
}