带循环随机访问的C++向量?

4
我希望有一个容器类在C++中,它与STL vector类的每个方面完全相同,唯一区别是[]运算符具有环绕行为。例如:
vector<int>  myVec;
myVec.push_back(5);
myVec.push_back(10);
myVec.push_back(15);

cout << myVec[ 5] << endl;   // would output 15
cout << myVec[ 1] << endl;   // would output 10
cout << myVec[-2] << endl;   // would output 10

是否已经存在这样的容器,或者是否可以在vector模板中重载或重新定义[]运算符?

我看过Boost循环缓冲区,但它不是以这种方式运行。


当点运算符成为标准时,这将变得非常容易。 - Revolver_Ocelot
那么 at 会保留其向量语义吗?另外,为什么 myVec[1] 打印的是 5 而不是 10?如果向量为空会发生什么? - Mark B
3个回答

3

这样的容器已经存在了吗?

至少在标准库中没有。

是否可以重载或重新定义vector模板中的[]运算符?

不可以,你不能重载或重新定义std::vector的[]运算符。

当然可以编写一个包装器,具有你所描述的行为的重载T& operator[](int pos)。像这样:

T& operator[](int pos) {
    std::vector<T>::size_type fancy_pos =
        pos < 0 ? data.size() + pos
                : pos - 1;
    return data[fancy_pos];
}

1
在C++中,容器的索引从0开始。
您可以将标准容器std::vector包装在一个类中,并以这样的方式重载运算符[],使得索引计算为index = index % size()index %= size()

1
你正在寻找一个循环缓冲区或圆形缓冲区。
Boost 库中有它们:

它们有时比使用std::deque自己编写的方法表现要好得多,例如ASIO中的这个示例:


更新

我认为 boost::circular_buffer 可能是您/应该想要的 - 因为它抽象了大多数通常需要此功能的任务的“如何”部分。但是,创建自己的适配器类型非常简单:

在 Coliru 上实时演示

#include <vector>

namespace mylib {
    template <typename T, typename Container = std::vector<T> >
    struct circular : Container {
        using Container::Container;
        using Container::operator =;

        auto& operator[](int i) const {
            // mixed signed/unsigned modulo is undefined
            while (i<0) i += Container::size();
            return Container::operator[](i % Container::size());
        }

        auto& operator[](int i) {
            while (i<0) i += Container::size();
            return Container::operator[](i % Container::size());
        }
    };
}

#include <iostream>

template <typename Whatever>
void test(Whatever const& data) {
    std::cout << data[ 5] << ", "; // would output 15
    std::cout << data[ 1] << ", "; // would output 10
    std::cout << data[-2] << std::endl; // would output 10
}

#include <string>
#include <deque>
int main() {
    test(mylib::circular<int>                                   { 5, 10, 15                }); 
    test(mylib::circular<std::string>                           { "five", "teen", "fiteen" }); 
    test(mylib::circular<std::string, std::deque<std::string> > { "five", "teen", "fiteen" }); 
    test(mylib::circular<int, std::deque<float> >               { 5, 10, 15                }); 
}

输出:

15, 10, 10
fiteen, teen, teen
fiteen, teen, teen
15, 10, 10

正如我在问题中所说的,我已经看过了Boost循环缓冲区,但它并没有实现我想要的行为。 - Rocketmagnet
我没有看到那个。然而,这只是简单的:在 Coliru 上实时运行(注意取模运算符周围的细微差别) - sehe
我不能使用Boost循环缓冲区的主要原因是它不接受范围在[0..n-1]之外的索引。 - Rocketmagnet
因为我对模板有点不熟悉:这个做的基本上是我想要的吗?它正在“覆盖”容器的[]运算符,并仍然提供所有其他容器的接口吗? - Rocketmagnet
没有问题。您没有提及任何版本。我可以轻松地在任何版本中编译它,只是稍微不太通用:c++11c++03。我回答中的版本是c++14。请注意,C++03并不会自动公开所有原始接口(因为继承构造函数不存在。您可能需要添加所需的构造函数)。 - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接