【问题标题】:Define tuples of numbers in d dimensions and accessing them在 d 维中定义数字元组并访问它们
【发布时间】:2019-03-29 00:28:33
【问题描述】:

我在玩一个看似曲解的想法,但我喜欢挑战,尽管它只是我发明的 C++ 练习。

任务是在 d 维中存储一系列 n 标量。想象一下,有一个向量、一个矩阵或一个由 n 个双精度数或整数或其他值组成的 3D 矩阵,并在运行时选择有多少个(手动计算,没什么特别的)。

例如,假设我想要一个长 4 的两个双精度数组(n == 2d == 1,在运行时我说我想要存储 4):

| d0 d1 | d0 d1 | d0 d1 | d0 d1 |

或 3 个双精度的 2D 2x4 网格(n == 3d == 2,在运行时我说我想要存储 2 * 4):

| d0 d1 d2 | d0 d1 d2 | d0 d1 d2 | d0 d1 d2 |
| d0 d1 d2 | d0 d1 d2 | d0 d1 d2 | d0 d1 d2 |

我如何访问例如网格的i, j-th 元素?使用运算符,当然......但我想编写一个 single 运算符,返回一个 引用元组 到三个双精度数。

给出一个想法:

template <std::size_t n, std::size_t d, typename Number>
class storage
{
public:

    // Magic tricks here?
    auto operator()(std::size_t i_0, ..., std::size_t i_d) -> std::tuple<n std::ref<Number>s>
    {

    }
};

问题是,我怎样才能在这里玩一些神奇的模板技巧来定义返回 同构类型元组one 运算符?

我发现特别难以考虑如何定义该元组,当然还有必须完全具有 d 参数的运算符。

至于用法,我希望能够像在 2D 示例中这样工作:

 // define a matrix of three doubles
 storage<3, 2, double> storageobj(8);

 // get a block, these should be references
 auto block = storageobj(0, 1);
 std::get<0>(block) = -123.456;

请注意,我现在并不关心如何访问内存中元素的实际实现,但返回类型的定义以及如何制作 operator() 是本练习的主要目标。

一些神奇的 TMP 可以在这里提供帮助吗?

【问题讨论】:

  • 您可以使用来自here 的信息创建这样一个元组。我会写下来,但我认为有人会更快。那么操作符 impl 本身就很简单了。
  • 如果类型相同,std::array 可能更合适。
  • 所以你想要一个由 n 维向量组成的 d 维矩阵,但是什么决定了矩阵的大小?此外,这听起来很像 boost::multi_array。
  • 我根据@Quentin 的评论修改了问题。我在运行时传递存储的长度,计算我需要多少块(行乘以列)。不,我想避免提升,只要我能享受编码的乐趣。
  • @senseiwa - 抱歉,但我不清楚所涉及的维度。哪个维度是已知的运行时和一个编译时(并且可能是某个地方的模板参数)。您可以添加一些预期声明/初始化和使用的示例吗?

标签: c++ templates multidimensional-array


【解决方案1】:

我将MultiArrayHow to allocate & access 3D, 4D, 5D arrays? 更改为std::array 的静态大小:

template <typename T, std::size_t Dim>
class MultiArray
{
public:

    explicit MultiArray(const std::array<std::size_t, Dim>& dimensions) :
        dimensions(dimensions),
        values(computeTotalSize(dimensions))
    {
        assert(!values.empty());
    }

    const T& get(const std::array<std::size_t, Dim>& indexes) const
    {
        return values[computeIndex(indexes)];
    }
    T& get(const std::array<std::size_t>& indexes)
    {
        return values[computeIndex(indexes)];
    }

    std::size_t computeIndex(const std::array<std::size_t, Dim>& indexes) const
    {
        size_t index = 0;
        size_t mul = 1;

        for (size_t i = 0; i != dimensions.size(); ++i) {
            assert(indexes[i] < dimensions[i]);
            index += indexes[i] * mul;
            mul *= dimensions[i];
        }
        assert(index < values.size());
        return index;
    }

    std::array<std::size_t, Dim> computeIndexes(std::size_t index) const
    {
        assert(index < values.size());

        std::array<std::size_t, Dim> res;

        std::size_t mul = values.size();
        for (std::size_t i = dimensions.size(); i != 0; --i) {
            mul /= dimensions[i - 1];
            res[i - 1] = index / mul;
            assert(res[i - 1] < dimensions[i - 1]);
            index -= res[i - 1] * mul;
        }
        return res;
    }

private:
    std::size_t computeTotalSize(const std::array<std::size_t, Dim>& dimensions) const
    {
        std::size_t totalSize = 1;

        for (auto i : dimensions) {
            totalSize *= i;
        }
        return totalSize;
    }

private:
    std::array<std::size_t, Dim> dimensions;
    std::vector<T> values;
};

然后添加层以使其适应您的界面(MultiArray 可能已经写成类似于storage_impl 以避免这种情况)。

要将array&lt;std::size_t, N&gt; 转换为std::size_t, .., std::size_t,我们使用std::index_sequence&lt;0, 1, 2, .., N - 1&gt; 来允许可变参数扩展。那么我们只需要将其转换为类型:

template <std::size_t, typename T>
using always_type = T;

template <std::size_t n, typename Seq, typename Number>
class storage_impl;

template <std::size_t n, typename Number, std::size_t ... Is>
class storage_impl<n, std::index_sequence<Is...>, Number>
{
public:
    storage_impl(always_type<Is, std::size_t>... dims) : array{{{dims...}}} ()

    std::array<Number, n>&
    operator()(always_type<Is, std::size_t>... indexes)
    {
        return array.get({{indexes...}});
    }

    const std::array<Number, n>&
    operator()(always_type<Is, std::size_t>... indexes) const
    {
        return array.get({{indexes...}});
    }

private:
    MultiArray<std::array<Number, n>, sizeof...(Is)> array;
};

最后:

template <std::size_t n, std::size_t d, typename Number>
using storage = storage_impl<n, std::make_index_sequence<d>, Number>;

用法类似于:

// define a 2D matrix of three doubles
 storage<3, 2, double> storageobj(2, 4); // matrix 2x4 of std::array<double, 3>

 auto&& block = storageobj(0, 1); // std::array<double, 3>&
 std::get<0>(block) = -123.456;   // or block[0] = -123.456

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-22
    • 2018-02-10
    • 2015-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多