【问题标题】:Multidimensional array: operator overloading多维数组:运算符重载
【发布时间】:2014-10-30 23:20:13
【问题描述】:

我有一个带有多维数组的类:

  • 可以用这个类创建一、二、...、n 维数组

  • 如果数组有 n 维,我想使用 n operator[] 来获取对象:

示例:

A a({2,2,2,2}]; 
a[0][1][1][0] = 5;

但数组不是指向其他向量等的指针向量...

所以我希望 operator[] 返回一个类对象,直到最后一个维度,然后返回一个整数

这是一个高度简化的代码,但它显示了我的问题:

我收到的错误:"[Error] cannot convert 'A::B' to 'int' in initialization"

#include <cstddef>     // nullptr_t, ptrdiff_t, size_t
#include <iostream>    // cin, cout...

class A {
    private:
        static int* a;
    public:
        static int dimensions;
        A(int i=0) { 
            dimensions = i;
            a = new int[5];
            for(int j=0; j<5; j++) a[j]=j; 
        };

        class B{
            public:
                B operator[](std::ptrdiff_t);
        };
        class C: public B{
            public:
                int& operator[](std::ptrdiff_t);
        };

        B operator[](std::ptrdiff_t);
};

//int A::count = 0;

A::B A::operator[] (std::ptrdiff_t i) {
    B res;
  if (dimensions <= 1){
    res = C();
}
  else{
    res = B();
  }
  dimensions--;
  return res;
}

A::B A::B::operator[] (std::ptrdiff_t i){
    B res;
    if (dimensions <=1){
        res = B();
    }
    else{
        res = C();
    }
    dimensions--;
    return res;
}

int& A::C::operator[](std::ptrdiff_t i){
    return *(a+i);
}


int main(){
    A* obj = new A(5);
    int res = obj[1][1][1][1][1];
    std::cout<< res << std::endl;
}

【问题讨论】:

  • 你知道 A* obj = new A(5); int res = obj[1][1][1][1][1];适用于指向 A 的指针? obj[1] 调用您的运算符,但访问 A 数组中的第二个元素!
  • @Oncaphillis,完全正确,在我的回答中甚至没有意识到这一点

标签: c++ arrays multidimensional-array operator-overloading


【解决方案1】:

operator[]obj[1][1]...[1] 中从左到右进行计算,因此obj[1] 返回一个B 对象。假设现在您只有int res = obj[1],那么您将分配给B 对象(或C 对象,在多次调用[] 的情况下)一个int,但没有从@987654330 转换@ 或 Cint。您可能需要编写一个转换运算符,例如

operator int()
{
   // convert to int here
}

对于ABC,因为重载的运算符不会被继承。

我只是通过为AB 编写这样的运算符来摆脱你的编译错误(当然我有链接错误,因为有未定义的函数)。

另外,请注意,如果您想编写类似obj[1][1]...[1] = 10 的内容,则需要重载operator=,因为同样没有从intA 或您的代理对象的隐式转换。

希望这是有道理的。

PS:另见@Oncaphillis 的评论!

【讨论】:

  • 嘿,我在 A、B 和 C 中添加了一个运算符 int(),在 A 中添加了一个 operator=,现在我收到一个新错误:(.text$_ZN1AC1Ei[_ZN1AC1Ei]+0x14 ): 对A::dimensions' ||| (.text$_ZN1AC1Ei[_ZN1AC1Ei]+0x25): undefined reference to A::a' 的未定义引用| | C:\Users\Marius K\Documents\collect2.exe [错误] ld 返回 1 个退出状态
  • 这是一个链接器错误,可能你有未定义的方法或静态变量。在后一种情况下,您需要在类之外实例化静态变量,例如 int A::mystatic;在你的情况下,指针必须设置为默认值,如 nullptr
【解决方案2】:

vsoftco 是完全正确的,如果你想实际访问你的元素,你需要实现一个重载运算符。如果您希望它是动态的,这是必要的,这就是您描述它的方式。我实际上认为这是一个有趣的问题,所以我实现了您所描述的模板。我认为它有效,但有些事情可能会略有偏差。代码如下:

template<typename T>
class nDimArray {
    using thisT = nDimArray<T>;

    T                    m_value;
    std::vector<thisT*>  m_children;
public:
    nDimArray(std::vector<T> sizes) {
        assert(sizes.size() != 0);
        int thisSize = sizes[sizes.size() - 1];
        sizes.pop_back();

        m_children.resize(thisSize);
        if(sizes.size() == 0) {
            //initialize elements
            for(auto &c : m_children) {
                c = new nDimArray(T(0));
            }
        } else {
            //initialize children
            for(auto &c : m_children) {
                c = new nDimArray(sizes);
            }
        }
    }
    ~nDimArray() {
        for(auto &c : m_children) {
            delete c;
        }
    }
    nDimArray<T> &operator[](const unsigned int index) {
        assert(!isElement());
        assert(index < m_children.size());
        return *m_children[index];
    }

    //icky dynamic cast operators
    operator T() {
        assert(isElement());
        return m_value;
    }
    T &operator=(T value) {
        assert(isElement());
        m_value = value;
        return m_value;
    }

private:
    nDimArray(T value) {
        m_value = value;
    }

    bool isElement() const {
        return m_children.size() == 0;
    }

    //no implementation yet
    nDimArray(const nDimArray&);
    nDimArray&operator=(const nDimArray&);
};

基本思想是这个类既可以充当数组的数组,也可以充当元素。这意味着实际上一个数组数组可以是一个元素数组!当您想要获取一个值时,它会尝试将其强制转换为一个元素,如果这不起作用,它只会抛出一个断言错误。

希望这是有道理的,当然,如果您有任何问题,请尽管问!事实上,我希望你能问,因为你描述的问题的范围比你想象的要大。

【讨论】:

    【解决方案3】:

    为此使用俄罗斯娃娃风格的模板类可能会很有趣。

    // general template where 'd' indicates the number of dimensions of the container
    // and 'n' indicates the length of each dimension
    // with a bit more template magic, we could probably support each
    // dimension being able to have it's own size
    template<size_t d, size_t n>
    class foo
    {
    private:
        foo<d-1, n> data[n];
    public:
        foo<d-1, n>& operator[](std::ptrdiff_t x)
        {
            return data[x];
        }
    };
    
    // a specialization for one dimension. n can still specify the length
    template<size_t n>
    class foo<1, n>
    {
    private:
        int data[n];
    public:
        int& operator[](std::ptrdiff_t x)
        {
            return data[x];
        }
    };
    
    int main(int argc, char** argv)
    {
        foo<3, 10> myFoo;
        for(int i=0; i<10; ++i)
            for(int j=0; j<10; ++j)
                for(int k=0; k<10; ++k)
                    myFoo[i][j][k] = i*10000 + j*100 + k;
    
        return myFoo[9][9][9]; // would be 090909 in this case
    }
    

    每个维度保留一个前一维度元素的数组。维度 1 使用跟踪一维 int 数组的基本特化。然后维度 2 将保留一个一维数组的数组,D3 将有一个二维数组的数组,等等。然后访问看起来与本机多维数组相同。在我的示例中,我在类中使用了数组。这使得 n 维数组的所有内存都是连续的,并且不需要在类内进行动态分配。但是,您也可以通过动态分配提供相同的功能。

    【讨论】:

    • 我认为模板是唯一的方法,因为在构造函数中动态指定尺寸根本没有任何意义。在编写 obj[1][1][1]... 等时,仍然需要在编译时提供并知道维度的数量。
    • @Oncaphillis,你确定吗?它不能与一些聪明的代理对象一起工作吗? OP 代码是有意义的,并且似乎可以修复。特别是,每当您调用 operator[] 时减小维度似乎是一个好主意,而当您调用 dimension==1 时,您只需返回对该元素的引用。我懒得修复代码,明天可能会尝试,因为这是一个很好的问题。
    • 能不能用不是问题。我认为一个人实际上可以让它发挥作用。但这有意义吗?假设您有一个多数组,您可以像数组(5)一样在运行时动态定义维数。稍后您可以通过 array[a][b][c][d][e] 访问元素。在代码中的这一点上,您一定已经意识到您正在处理在编译时的五个维度。你赢了什么?最好将维度作为模板 arg 提供。
    • @Oncaphillis 我不同意,有时您可能需要从文件中读出一个张量,因此您在编译时无法知道维数。实际上,我非常希望能够在运行时指定维数,这与 boost::multi_array 不同,至少在我的工作中我发现它非常有用。您还可以在运行时检查您是否访问了超过 N 个维度(当然,这会降低速度)。
    • @vsoftco 当然它很有用,但是使用代理对象和 [] 运算符的级联访问多数组中的具体单元没有多大意义,因为您需要知道您实际上必须级联多少个这些运算符才能到达您的具体单元。最好隐藏必须访问具体单元格的函数的具体维数。
    猜你喜欢
    • 2013-05-15
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    相关资源
    最近更新 更多