在 C++ 中是否有一种简单的方法来获取数组的切片?
也就是说,我有
array<double, 10> arr10;
并且想要获取由
arr10
的前五个元素组成的数组:
array<double, 5> arr5 = arr10.???
(除了通过迭代第一个数组来填充它)
std::array
的构造函数是隐式定义的,因此您无法使用另一个容器或迭代器的范围来初始化它。您可以获得的最接近的是创建一个辅助函数,该函数负责构建过程中的复制。这允许单相初始化,这就是我相信您想要实现的目标。
template<class X, class Y>
X CopyArray(const Y& src, const size_t size)
{
X dst;
std::copy(src.begin(), src.begin() + size, dst.begin());
return dst;
}
std::array<int, 5> arr5 = CopyArray<decltype(arr5)>(arr10, 5);
您还可以使用类似
std::copy
的内容或自己迭代副本。
std::copy(arr10.begin(), arr10.begin() + 5, arr5.begin());
当然。写下这个:
template<int...> struct seq {};
template<typename seq> struct seq_len;
template<int s0,int...s>
struct seq_len<seq<s0,s...>>:
std::integral_constant<std::size_t,seq_len<seq<s...>>::value> {};
template<>
struct seq_len<seq<>>:std::integral_constant<std::size_t,0> {};
template<int Min, int Max, int... s>
struct make_seq: make_seq<Min, Max-1, Max-1, s...> {};
template<int Min, int... s>
struct make_seq<Min, Min, s...> {
typedef seq<s...> type;
};
template<int Max, int Min=0>
using MakeSeq = typename make_seq<Min,Max>::type;
template<std::size_t src, typename T, int... indexes>
std::array<T, sizeof...(indexes)> get_elements( seq<indexes...>, std::array<T, src > const& inp ) {
return { inp[indexes]... };
}
template<int len, typename T, std::size_t src>
auto first_elements( std::array<T, src > const& inp )
-> decltype( get_elements( MakeSeq<len>{}, inp ) )
{
return get_elements( MakeSeq<len>{}, inp );
}
编译时
indexes...
进行重新映射,MakeSeq 生成从 0 到 n-1 的 seq。
实例.
更新到最新版本的 C++:
template<class Seq, std::size_t N>
struct add_to_seq;
template<class Seq, std::size_t N>
using add_to_seq_t = typename add_to_seq<Seq, N>::type;
template<std::size_t...Is, std::size_t N>
struct add_to_seq<std::index_sequence<Is...>, N>
{
using type=std::index_sequence<(Is+N)...>;
};
template<std::size_t Min, std::size_t Max>
struct make_seq {
using type=add_to_seq_t< std::make_index_sequence<Max-Min>, Min >;
};
template<std::size_t Min, std::size_t Max>
using make_seq_t = typename make_seq<Min,Max>::type;
template<std::size_t src, typename T, std::size_t... indexes>
std::array<T, sizeof...(indexes)> get_elements( std::index_sequence<indexes...>, std::array<T, src> const& inp ) {
return { inp[indexes]... };
}
template<std::size_t len, typename T, std::size_t src>
auto first_elements( std::array<T, src> const& inp )
{
return get_elements( std::make_index_sequence<len>{}, inp );
}
实例.
(更现代的版本生成 O(n) 总编译时间模板类型 - 旧版本在编译时模板生成中为 O(n^2)。这意味着编译速度更快,并且不太可能达到编译器资源限制。)
这支持任意一组索引(通过
get_elements
)和前 n 个索引(通过 first_elements
)。
用途:
std::array< int, 10 > arr = {0,1,2,3,4,5,6,7,8,9};
std::array< int, 6 > slice = get_elements(arr, seq<2,0,7,3,1,0>() );
std::array< int, 5 > start = first_elements<5>(arr);
这避免了所有循环,无论是显式的还是隐式的。
2018更新,如果你需要的是
first_elements
:
使用 C++14 的更少样板解决方案(建立在 Yakk 14 之前的答案上,并从 “拆包”元组来调用匹配的函数指针中窃取)
template < std::size_t src, typename T, int... I >
std::array< T, sizeof...(I) > get_elements(std::index_sequence< I... >, std::array< T, src > const& inp)
{
return { inp[I]... };
}
template < int N, typename T, std::size_t src >
auto first_elements(std::array<T, src > const& inp)
-> decltype(get_elements(std::make_index_sequence<N>{}, inp))
{
return get_elements(std::make_index_sequence<N>{}, inp);
}
仍然无法解释为什么这有效,但它确实有效(对我来说在 Visual Studio 2017 上)。
这个答案可能迟到了……但我只是在玩弄切片——所以这是我自制的
std::array
切片。
当然,这有一些限制,并且最终并不通用:
但是下面代码的一个很好的功能是,您可以切片切片......
// ParCompDevConsole.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "pch.h"
#include <cstdint>
#include <iostream>
#include <array>
#include <stdexcept>
#include <sstream>
#include <functional>
template <class A>
class ArraySliceC
{
public:
using Array_t = A;
using value_type = typename A::value_type;
using const_iterator = typename A::const_iterator;
ArraySliceC(const Array_t & source, size_t ifirst, size_t length)
: m_ifirst{ ifirst }
, m_length{ length }
, m_source{ source }
{
if (source.size() < (ifirst + length))
{
std::ostringstream os;
os << "ArraySliceC::ArraySliceC(<source>,"
<< ifirst << "," << length
<< "): out of bounds. (ifirst + length >= <source>.size())";
throw std::invalid_argument( os.str() );
}
}
size_t size() const
{
return m_length;
}
const value_type& at( size_t index ) const
{
return m_source.at( m_ifirst + index );
}
const value_type& operator[]( size_t index ) const
{
return m_source[m_ifirst + index];
}
const_iterator cbegin() const
{
return m_source.cbegin() + m_ifirst;
}
const_iterator cend() const
{
return m_source.cbegin() + m_ifirst + m_length;
}
private:
size_t m_ifirst;
size_t m_length;
const Array_t& m_source;
};
template <class T, size_t SZ>
std::ostream& operator<<( std::ostream& os, const std::array<T,SZ>& arr )
{
if (arr.size() == 0)
{
os << "[||]";
}
else
{
os << "[| " << arr.at( 0 );
for (auto it = arr.cbegin() + 1; it != arr.cend(); it++)
{
os << "," << (*it);
}
os << " |]";
}
return os;
}
template<class A>
std::ostream& operator<<( std::ostream& os, const ArraySliceC<A> & slice )
{
if (slice.size() == 0)
{
os << "^[||]";
}
else
{
os << "^[| " << slice.at( 0 );
for (auto it = slice.cbegin() + 1; it != slice.cend(); it++)
{
os << "," << (*it);
}
os << " |]";
}
return os;
}
template<class A>
A unfoldArray( std::function< typename A::value_type( size_t )> producer )
{
A result;
for (size_t i = 0; i < result.size(); i++)
{
result[i] = producer( i );
}
return result;
}
int main()
{
using A = std::array<float, 10>;
auto idf = []( size_t i ) -> float { return static_cast<float>(i); };
const auto values = unfoldArray<A>(idf);
std::cout << "values = " << values << std::endl;
// zero copy slice of values array.
auto sl0 = ArraySliceC( values, 2, 4 );
std::cout << "sl0 = " << sl0 << std::endl;
// zero copy slice of the sl0 (the slice of values array)
auto sl01 = ArraySliceC( sl0, 1, 2 );
std::cout << "sl01 = " << sl01 << std::endl;
return 0;
}