访问多维 std::array 的可变参数扩展

Posted

技术标签:

【中文标题】访问多维 std::array 的可变参数扩展【英文标题】:Variadic expansion to access multi-dimensional std::array 【发布时间】:2022-01-16 01:29:26 【问题描述】:

假设如下multi_array结构:

template <typename type, std::size_t... sizes>
struct multi_array

  using storage_type    = typename storage_type<type, sizes...>::type;
  using value_type      = type;
  using size_type       = std::array<std::size_t   , sizeof...(sizes)>;
  using difference_type = std::array<std::ptrdiff_t, sizeof...(sizes)>;
  using reference       = value_type&;
  using const_reference = const value_type&;
  // ...

  storage_type data_  ;
  size_type    sizes_ sizes...;


// Example usage: 
// multi_array<float, 3, 4, 5> three_dimensional_float_array;
// storage_type will be std::array<std::array<std::array<float, 5>, 4>, 3>
// size_type    will be std::array<std::size_t, 3>

地点:

// Helpers to create a std::array<std::array<...>> from a pack expansion.
template <typename  type,                   std::size_t... sizes>
struct storage_type;
template <typename _type, std::size_t size, std::size_t... sizes>
struct storage_type<_type, size, sizes...>

  using type = std::array<typename storage_type<_type, sizes...>::type, size>;
;
template <typename _type>
struct storage_type<_type>

  using type = _type;
;

我现在正在尝试实现 .at() 函数:

  [[nodiscard]]
  constexpr reference       at   (const size_type& position)
  
    // This has to be:
    // data_[position[0]][position[1]][position[2]]...;
  

折叠表达式在这种情况下不起作用,因此需要递归解决方案。

我认为是这样,但似乎无法得出正确的答案:

[[nodiscard]]
constexpr reference at(const size_type& position)

  return access_type<value_type, sizeof...(sizes)>::at(data_, position);


template <typename type, std::size_t size, std::size_t index = 0>
struct access_type

  template <typename array_type>
  auto at (const array_type& array, const std::array<std::size_t, size>& position, type& current) // Problem: Reference is not a type& for intermediates.
  
    if constexpr (index == 0)
      current = &array;

    if constexpr (index + 1 != size)
    
      return access_type::at<type, size, index + 1>(array, position, current);
    
  
;

【问题讨论】:

仅供参考,multi_array 实际上并不满足容器的要求,因为size_type 需要是无符号整数类型,difference_type 需要是有符号整数类型。请参阅标准中的 [tab:container.req],或 cppreference 上的 Container position 是对一个孤独的size_type 的引用。 position[0] 不计算。 positionstd::array&lt;std::size_t, sizeof...(sizes)&gt; multi_array 定义为带有operator[] 的单个std::array 的包装器可能会简单得多,它提供多维索引而不是实际的嵌套数组。通过这种方式,您还可以免费获得所有必需的容器类型别名。 @Brian 如果曾经考虑使用 std::multi_array,则必须修改标准。对第二条评论的回答:与索引到 std::array<:array>> 时的 N 读取相比,这将需要对每个 .at() 操作进行算术运算。写yes会更容易,效率也更低。 【参考方案1】:

这是我的看法:

namespace _details

    template<class InputIt, class OutputIt>
    OutputIt partial_product(InputIt first, InputIt last, OutputIt output)
     *output++ = 1; return partial_sum(first, last, output, std::multiplies<>); 

    // cache-friendly:
    // neighbor objects within the right-most coordinate are neighbors in memory
    template<class TDim, class TCoord>
    auto coordinates_to_index(TDim const& dimensions, TCoord const& coords)
    
        std::array<std::size_t, dimensions.size()> dimension_product;
        using std::crbegin, std::crend, std::prev;
        partial_product(crbegin(dimensions), prev(crend(dimensions)), begin(dimension_product));
        return std::inner_product(cbegin(dimension_product), cend(dimension_product), crbegin(coords), 0);
    

它将元组(x,y,z,...) 转换为一维索引x*N + y*K + ...,其中NK、...根据容器的总尺寸进行选择。

并用在我自己的多维缓存友好容器中:

    /**
     * @brief Returns a reference to the element at coordinates.
     * @param coordinates Coordinates of the element to return
     *
     * No bounds checking is performed; if @c coordinates are outside od
     * the matrix dimensions, the behavior is undefined.
     */
    template<class... Args>
    T const& operator()(Args... coordinates) const
     return _data[_details::coordinates_to_index(dimensions, std::arraycoordinates...)]; 

来源:yscialom/matrix/matrix.hpp on my GitHub(欢迎 PR)

【讨论】:

【参考方案2】:

大概是这样的:

template <size_t level, size_t max_level>
struct Access 
  template<typename Store>
  static reference At(Store& store, const size_type& position) 
    return Access<level + 1, max_level>::At(
        store[position[level]], position);
  
;

template <size_t level>
struct Access<level, level> 
  template<typename Store>
  static reference At(Store& store, const size_type&) 
    return store;
  
;


reference at(const size_type& position)

  return Access<0, sizeof...(sizes)>::At(data_, position);

【讨论】:

谢谢,这也为如何实现类似于 std::array 的其他成员提供了很多见解。

以上是关于访问多维 std::array 的可变参数扩展的主要内容,如果未能解决你的问题,请参考以下文章

可变参数模板到数组访问的无递归扩展

变长 std::array 像

如何通过可变参数模板将多个构造函数参数转发到数组初始值设定项列表?

传递可变大小的多维数组

从可变参数模板中扩展的 decltype 继承

Scala集合