子阵列模板

时间:2016-11-30 16:05:26

标签: c++ templates

让我用代码解释一下我在寻找什么。让我们假设我们有一个包装数组的模板:

template <typename T, std::size_t SIZE>
struct wrap
{
    using value_type = T;
    using wrap_type = wrap;

    static constexpr auto size = SIZE;
    T data[size]{};
};

我想在wrap模板中添加一个函数,这个函数应该返回一个能够操作wrap中包含的数组特定部分的对象,该函数应该知道哪个子数组开始,它将有多少元素,所以我想到了这样的函数:

template <typename T, std::size_t SIZE>
struct wrap
{
    using value_type = T;
    using wrap_type = wrap;

    static constexpr auto size = SIZE;
    T data[size]{};

    template <std::size_t START, std::size_t COUNT>
    ???? sub() { ... }
};

我不希望????返回的wrap::sub对象有一些指针而是一个静态数组,所以我的方法是:

template <typename T, std::size_t SIZE>
struct wrap
{
    using value_type = T;
    using wrap_type = wrap;

    static constexpr auto size = SIZE;
    T data[size]{};

    template <std::size_t START, std::size_t COUNT>
    struct sub_array
    {
        using value_type = wrap_type::value_type *;

        static constexpr auto start = START;
        static constexpr auto count = COUNT;

        value_type data[count]{};
    };

    template <std::size_t START, std::size_t COUNT>
    sub_array<START, COUNT> sub() { ... }
};

问题在于:我应该在wrap::sub写一下什么?我很确定使用std::integer_sequence的方法应该是可行的,但我缺乏经验,因此我甚至不知道该尝试什么。我试图在2中使用两个模板参数设置wrap::sub的值,并且它按预期工作:

template <std::size_t START, std::size_t SIZE>
sub_array<START, SIZE> sub() { return { &data[START], &data[START + 1u] }; }

我已经测试了它,如下所示:

wrap<int, 9> w{1, 2, 3, 4, 5, 6, 7, 8, 9};

// x is the view [3, 4]
auto x = w.sub<2, 2>();

std::cout << *x.data[0] << '\n'; // shows 3

所以,概念上wrap::sub的主体应该是:

return { &data[START + 0], &data[START + 1], ... &data[START + n] };

我应该如何达到上述效果?

2 个答案:

答案 0 :(得分:1)

这解决了上述问题:

template<class=void, std::size_t...Is>
auto indexer( std::index_sequence<Is...> ) {
  return [](auto&& f){
    return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
  };
}
template<std::size_t N>
auto indexer() {
  return indexer( std::make_index_sequence<N>{} );
}

这使您可以创建一个可扩展的编译时整数参数包,而无需每次都编写自定义帮助函数。

它是有效的C ++ 14,但有些编译器(如MSVC)声称是C ++ 14编译器而不是C ++ 14编译器。

template <std::size_t START, std::size_t SIZE>
sub_array<START, SIZE> sub() const {
  auto index = indexer<SIZE>();
  return index(
    [&](auto...Is)->sub_array<START, SIZE>
    {
      return {data[START+Is]...};
    }
  );
}

indexer<N>返回一个索引器,在传递lambda时,使用std::integral_constant<std::size_t, 0>std::integral_constant<std::size_t, N-1>调用它。

然后可以在创建indexer

的函数中内联展开此包

在您提到的评论中,您希望修改子数组以反映在原始数组中。

您的设计不允许这样做。子数组是原始数组切片的副本。

正确的方法是让你的子数组成为原始数组的一对指针。

template<class T>
struct array_view {
  T* b = 0;
  T* e = 0;

  T* begin() const { return b; }
  T* end() const { return e; }

  T& operator[](std::size_t i)const { return begin()[i]; }

  bool empty() const { return begin()==end(); }
  std::size_t size() const { return end()-begin(); }

  array_view( T* s, T* f ):b(s), e(f) {}
  array_view( T* s, std::size_t l):array_view(s, s+l) {}

  array_view()=default;
  array_view(array_view const&)=default;
  array_view& operator=(array_view const&)=default;
};

array_view是一种讨论数组切片的简洁方法。

如果你想要大步前进,你必须做更多的工作。

答案 1 :(得分:1)

  

我非常确定使用std::integer_sequence的方法应该是可行的......

这是:

template <typename T, std::size_t SIZE>
struct wrap
{
    using value_type = T;
    using wrap_type = wrap;

    static constexpr auto size = SIZE;
    T data[size]{};

    template <std::size_t START, std::size_t COUNT>
    struct sub_array
    {
        using value_type = wrap_type::value_type *;

        static constexpr auto start = START;
        static constexpr auto count = COUNT;

        value_type data[count]{};
    };

    template <std::size_t START, std::size_t COUNT>
    constexpr sub_array<START, COUNT> sub() {
        static_assert(START + COUNT <= SIZE, "sub_array is out of range");
        // The argument makes an instance of integer sequence from 0 to COUNT-1
        return sub_impl<START, COUNT>(std::make_index_sequence<COUNT>{});
    }

private:

    template <std::size_t START, std::size_t COUNT, std::size_t ... Is>
    constexpr sub_array<START, COUNT> sub_impl(std::index_sequence<Is...>) {
        // Arithmetic in argument list expansion is pretty valid.
        return {&data[Is+START]...};
    }
};

现在有以下几行:

wrap<double, 10> ds{1,2,3,4,5,6,7,8,9,10};
auto s = ds.sub<2,3>();
std::cout << *s.data[0] << std::endl;

打印3

此代码完全适用于您的结构documentation examples。可能会缩短它。并且我确信可以在代码中声明更多静态(编译时)断言。

最后,根据@ Yakk的评论,sub的这种实现会返回一个子数组副本,而不是视图。