可变参数扩展以访问多维 std::array
Variadic expansion to access multi-dimensional std::array
假设以下 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);
}
}
};
大概是这样的:
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);
}
这是我的看法:
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 + ...
,其中 N
、K
、...根据容器总尺寸选择。
并在我自己的多维缓存友好容器中使用:
/**
* @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::array{coordinates...})]; }
来源:yscialom/matrix/matrix.hpp on my GitHub(欢迎 PR)
假设以下 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);
}
}
};
大概是这样的:
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);
}
这是我的看法:
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 + ...
,其中 N
、K
、...根据容器总尺寸选择。
并在我自己的多维缓存友好容器中使用:
/**
* @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::array{coordinates...})]; }
来源:yscialom/matrix/matrix.hpp on my GitHub(欢迎 PR)