C++如何为列表重载[][]运算符

5

我有一个Matrix类,其中包含一个成员变量std::list<Element> listMatrix;Element是一个类,它有3个int类型的成员变量line, column, value。我将矩阵中不为0的元素的行、列和值保存在列表中。我想重载operator [][],这样我就可以像这样做:Matrix a; a[2][3] = 5;。我知道你不能直接overload [][]


1
OT:你使用 list 的原因是什么?由于数据的局部性(即较少的缓存未命中),vector 很可能更快。而且我不会经常期望矩阵在中间插入元素... - 463035818_is_not_a_number
我需要使用 list 因为问题规定了必须使用列表。 - Ovidiu Firescu
2
@formerlyknownas_463035818 我猜这是一个稀疏矩阵格式。 - lisyarus
2个回答

9

请对 Element& operator()(int, int) (以及其const版本)进行重载,以便您可以编写如下代码:

matrix(2, 3) = 5;

如果你绝对需要使用[2][3]的语法,你需要定义一个代理类,这样matrix[2]就会返回一个代理值,proxy[3]则会返回所需的引用。但这会带来很多问题。基本思路如下:
class naive_matrix_2x2
{
    int data[4];

    struct proxy
    {
          naive_matrix_2x2& matrix;
          int x;
          int& operator[](int y) { return matrix.data[x*2+y]; }
    };
public:
    proxy operator[](int x) { return {*this, x}; }
};

完整演示:https://coliru.stacked-crooked.com/a/fd053610e56692f6

我需要像[2][3]这样做。我在其他问题中读到了关于代理类的内容,但是我不知道它是如何工作以及如何实现的。是否有更深入或更长的解释,以及如何解决相关问题?另外,在重载[][]之后,当我写matrix[2][3] = 0时,是否可能使其从列表中删除元素(2,3,value),因为它变成了0,而我只想在列表中保留非零元素。 - Ovidiu Firescu
我认为你把事情搞得过于复杂了。这需要第二层代理来检测何时写入0(通过重载operator=)。这不是一个好主意(商标)。 - YSC
我明白了。我知道,但我只是想考虑所有可能的情况。暂时我会这样做,感谢你的回答。 - Ovidiu Firescu
2
你最好给代理起一个真实的名字 - class Row 是我给我的命名方式。 - MSalters
这是一个聪明的解决方案。将多维矩阵定义为一维对于跳转矩阵和内存管理非常有用。我建议这个问题的提问者提供额外的索引公式,包括行/列/高度,以便人们更容易地理解矩阵(a,b,c...)的工作原理。 - Danilo
显示剩余2条评论

4

列表不适合使用下标运算符作为容器,因为它没有直接访问其元素的方法,必须通过移动迭代器来查找。因此,使用该运算符效率低下。

最好使用标准容器 std::vector,它已经具有下标运算符。

尽管如此,回答您的问题,可以按照以下方式定义该运算符。您可以在运算符中添加异常处理,然后索引将指向列表之外的位置。

#include <iostream>
#include <list>

struct A
{
    int x, y, z;
    int & operator []( size_t n ) 
    {
        return n == 0 ? x : n == 1 ? y : z;             
    }

    const int & operator []( size_t n ) const
    {
        return n == 0 ? x : n == 1 ? y : z;             
    }
};

struct B
{
    std::list<A> lst;
    A & operator []( size_t n )
    {
        auto it = std::begin( lst );
        for ( ; n; n-- ) std::advance( it, 1 ); 
        return *it;
    }

    const A & operator []( size_t n ) const
    {
        auto it = std::begin( lst );
        for ( ; n; n-- ) std::advance( it, 1 ); 
        return *it;
    }
};

int main()
{
    B b = { { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } } };

    std::cout << b[0][0] << '\n';
    std::cout << b[0][1] << '\n'; 
    std::cout << b[0][2] << '\n'; 

    b[2][1] += 20;

    std::cout << b[2][1] << '\n'; 
}

程序输出为:
1
2
3
28

我知道列表不太合适,但我需要使用它。此外,程序不能按照我在问题中所需的方式工作。对于您的示例,在列表中只有3个元素b[1][2] b[4][5] b[7][8],这三个元素的输出应为3 6 9,其余元素应为0,因为它们不在列表中。感谢您的回答。 - Ovidiu Firescu
@Ovidiu Firescu,我展示了如何定义运算符。你可以将这个例子作为你的运算符重载的基础。你在评论中描述的内容与你的问题无关。 - Vlad from Moscow
我明白了,这是一个很好的例子,可以帮助理解如何为列表实现[][],谢谢。 - Ovidiu Firescu

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