让我用代码解释一下我在寻找什么。让我们假设我们有一个包装数组的模板:
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] };
我应该如何达到上述效果?
答案 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
的这种实现会返回一个子数组副本,而不是视图。