9

Is there an easy way to get a slice of an array in C++?

I.e., I've got

array<double, 10> arr10;

and want to get array consisting of five first elements of arr10:

array<double, 5> arr5 = arr10.???

(other than populating it by iterating through first array)

4

4 回答 4

7

的构造函数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());
于 2013-05-20T03:56:37.057 回答
6

当然。写了这个:

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 将 seq 从 0 变为 n-1。

活生生的例子

这支持任意一组索引 (via get_elements) 和第一个 n (via 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);

这避免了所有循环,无论是显式的还是隐式的。

于 2013-05-20T05:12:48.587 回答
0

2018 年更新,如果您只需要first_elements

使用 C++14 的较少样板解决方案(建立在Yakk 的 pre-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 上)。

于 2018-07-10T16:01:00.540 回答
0

这个答案可能会迟到......但我只是在玩切片 - 所以这是我家的小std::array切片。

当然,这有一些限制,最终并不普遍:

  • 从中获取切片的源数组不得超出范围。我们存储对源的引用。
  • 我首先在寻找常量数组切片,并没有尝试将此代码扩展到 const 和非 const 切片。

但是下面代码的一个不错的功能是,您可以将切片切片...

// 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;
}
于 2018-10-12T23:56:25.860 回答