C++でスライスみたいなやつ

Twitterを見ているとTLの者がよく「C++でスライスしてぇ~~~」と言っている。私はたまたまC++が書けるので、なんとなくスライスのようなものを実装してみようと思う。

まずスライスの文法からおさらいする。RustやGoではスライスが言語機能なのでかなり簡便に書ける。例えばlist[..]とかlist[:]みたいに。ではこのことをC++でやろうとすればどのように書けるだろうか? [..]とか[:]という文法はC++ではどう頑張ってもコンパイルエラーになってしまうため、別の手段が必要だ。かといって[0,100]なんて書いてしまえば、[100]と同じ意味になってしまう。何とかしてoperator[]に二つの値を渡したいが、簡便に記述したい。 欲を言えば最初の要素から最後の要素までスライスするなら引数を省略したい。

う~~~~ん、、、正直何も思いつかないがstd::pairを使うのが一番いい気がする。

for(auto& i : list[{ 0, 100 }]

みたいに書ければいいのかなぁ。あんまりきれいではないが、C++ではこれが限界か。

実装

#include <type_traits>
#include <iostream>
#include <vector>

template<class Container>
concept bool Iteratable = requires() {
    typename Container::iterator;
    typename Container::const_iterator;
};

template<Iteratable Container>
class sliceable {
    Container& co;
public:
    sliceable(Container& c)
        : co(c)
    {}
    struct sliced {
        using iterator = Container::iterator;
        iterator first;
        iterator last;
        sliced(iterator first, iterator last)
            : first{ first }
            , last{ last }
        {}
        iterator begin() const { return first; }
        iterator end() const { return last; }
    };

    sliced operator[] (const std::pair<typename Container::size_type, typename Container::size_type>& range) {
        auto second = std::min(co.size(), range.second);
        auto first = std::min(second, range.first);
        return sliced{ std::next(co.begin(), first),
                       std::next(co.begin(), second) };
    }
};

int main() {
    static_assert(Iteratable<std::vector<int>>);
    std::vector<int> v(10, 10);
    sliceable<std::vector<int>> s{v};
    for(auto& i : s[{0,3}])
        std::cout << i << '\n';
}

C++でスライスはやはり若干の無理があるようだ。C++20以降でoperator[]が複数の引数を取れたり、引数を省略できるようになるのを待とう。