一个类 A 有成员 double[][],我希望直接能在对象上进行类似这样的操作:

A a;

double x = a[0][0];

该怎么实现?


需要一个 helper 类,大约是:

class ARowHelper {
friend class A;
A a_;
size_t r_;
ARowHelper(A a, size_t r) : a_(a), r_(r) {}
public:
double operator[](size_t c) { return a_.data_[r_][c]; }
};

class A {
double** data_;
public:
ARowHelper operator[](size_t r) { return ARowHelper(*this, r); }
};

实际上还要考虑 const-ness 等问题。


operator[]返回一个double*或者不就够了,使用时[][]里的第二个[]是指针自带。

不过存多维数组的话我还是推荐扁平化……

C++怎么实现效率尽可能高地带边界检查的多维数组模板类??

www.zhihu.com图标

与其重载[][]不如重载()。你所需要的操作它都能满足,而且也不失优雅。

[][]这种表达,很容易联想到一种数据类型,那就是矩阵:Matrix。

而关于Matrix该如何优雅的重载运算符。isocpp官方恰好有个demo,那就是重载 operator () 而不是[]:

class Matrix {
public:
Matrix(unsigned rows, unsigned cols);
double operator() (unsigned row, unsigned col); // Subscript operators often come in pairs
double operator() (unsigned row, unsigned col) const; // Subscript operators often come in pairs
// ...
~Matrix(); // Destructor
Matrix(const Matrix m); // Copy constructor
Matrix operator= (const Matrix m); // Assignment operator
// ...
private:
unsigned rows_, cols_;
double* data_;
};
inline
Matrix::Matrix(unsigned rows, unsigned cols)
: rows_ (rows)
, cols_ (cols)
//, data_ ← initialized below after the if...throw statement
{
if (rows == 0 || cols == 0)
throw BadIndex("Matrix constructor has 0 size");
data_ = new double[rows * cols];
}
inline
Matrix::~Matrix()
{
delete[] data_;
}
inline
double Matrix::operator() (unsigned row, unsigned col)
{
if (row &>= rows_ || col &>= cols_)
throw BadIndex("Matrix subscript out of bounds");
return data_[cols_*row + col];
}
inline
double Matrix::operator() (unsigned row, unsigned col) const
{
if (row &>= rows_ || col &>= cols_)
throw BadIndex("const Matrix subscript out of bounds");
return data_[cols_*row + col];
}

int main()
{
Matrix m(10,10);
m(5,8) = 106.15;
std::cout &

引用自:https://isocpp.org/wiki/faq/operator-overloading#matrix-subscript-op

[][] 的通过中间类还能实现。假如你日后需要[][][]呢?这并不稀奇,比如表示更高维的向量空间。

operator ()比之其他operator的一大好处就是不限制参数个数,并且()看著也并不突兀。


←_← 想好了,支持a[0][0]就意味著(a[0])[0][](auto r) { return r[0]; }(a[0])都要有同样的语义。尤其是后一个,用户可能会拿r做各种各样的操作,作为实现者要想好哪些操作有什么后果。

如果不想承受这种心智负担的话,还是不要支持这种写法为好。a.at(0, 0)也没多长。

支持a[0][0]的方式其他答主都解释过了,就是让operator[]返回一个proxy。


模板大法好,可以套娃套一个任意维度的出来。上星期刚好写了一个。

用起来像这样:

支持直接用index访问,和(只读)插值访问。

代码:

#pragma region Table

#pragma region ToolStructs
template&
struct Multiplicative;
template&
struct Multiplicative& {
enum { value = First * Multiplicative&::value };
};
template&
struct Multiplicative& {
enum { value = First };
};
template&
struct Count;
template&
struct Count& {
enum { value = Count&::value + 1 };
};
template&
struct Count& {
enum { value = 1 };
};
#pragma endregion

template&
struct TableBlock;
template&
struct TableBlock&
{
DataType* ptr;
DataType operator[](const int x) {
return *(ptr + x);
}
DataType Evaluate(const double* coord) {
double x = coord[0];

int size_sub_1 = size - 1;
double c_x = max(0.0, min(x, 1.0));
int index = c_x * size_sub_1;
int index_add_1 = min(index + 1, size_sub_1);

return lerp(ptr[index], ptr[index_add_1], c_x * size_sub_1 - index);
}
TableBlock(DataType* ptr) : ptr(ptr) {}
};
template&
struct TableBlock&
{
DataType* ptr;
TableBlock& operator[](const int x) {
int size_sub_1 = size - 1;
auto offset = max(0, min(x, size_sub_1)) * Multiplicative&::value;
return TableBlock&(ptr + offset);
}
DataType Evaluate(const double* coord) {
double x = coord[0];
int size_sub_1 = size - 1;
double c_x = max(0.0, min(x, 1.0));
int index = c_x * size_sub_1;
int index_add_1 = min(index + 1, size_sub_1);

DataType res0 = operator[](index).Evaluate(coord+1);
DataType res1 = operator[](index_add_1).Evaluate(coord+1);

return lerp(res0, res1, c_x * size_sub_1 - index);
}

TableBlock(DataType* ptr) : ptr(ptr) {}
};

template&
struct Table
{
TableBlock&::value, size, rest...&> table;
Table() : table(nullptr) {}
~Table() {
if (table.ptr != nullptr)
delete(table.ptr);
}
void Init() {
if (table.ptr != nullptr) delete(table.ptr);
table.ptr = new DataType[Multiplicative&::value];
}
TableBlock&::value - 1, rest...&> operator[](const int x) {
return table[x];
}

DataType operator[](const initializer_list& coord) {
return table.Evaluate(coord.begin());
}

void ClearWith(const DataType data) {
for (int i = 0; i &< Multiplicative&::value; i++) {
table.ptr[i] = data;
}
}

void Save(string name) {
ofstream ofs("./" + name, ios::out | ios::binary);

string format = "(%d";
for (int i = 1; i &< Count&::value; i++) {
format += ",%d";
}
format += ")
";
char tmp[99999];
sprintf(tmp, format.c_str(), size, rest...);
format = string(tmp);

ofs.write((char*)format, sizeof(format));
ofs.write((char*)table.ptr, sizeof(DataType) * Multiplicative&::value);

ofs.close();
}

Table(string name) : table(new DataType[Multiplicative&::value]) {
ifstream ifs("./" + name, ios::in | ios::binary);

string format = "(%d";
for (int i = 1; i &< Count&::value; i++) {
format += ",%d";
}
format += ")
";
char tmp[99999];
sprintf(tmp, format.c_str(), size, rest...);
format = string(tmp);

ifs.read((char*)format, sizeof(format));
ifs.read((char*)table.ptr, sizeof(DataType) * Multiplicative&::value);

ifs.close();
}

void operator=(Table& c) {
table.ptr = c.table.ptr;
c.table.ptr = nullptr;
}
};
template&
struct Table&
{
TableBlock& table;
Table() : table(nullptr) {}
~Table() {
if (table.ptr != nullptr)
delete(table.ptr);
}
void Init() {
if (table.ptr != nullptr) delete(table.ptr);
table.ptr = new new DataType[size];
}
DataType operator[](const int x) {
return table[x];
}

DataType operator[](const initializer_list& coord) {
return table.Evaluate(coord.begin());
}

void ClearWith(const DataType data) {
for (int i = 0; i &< size; i++) { table.ptr[i] = data; } } void Save(string name) { ofstream ofs("./" + name, ios::out | ios::binary); string format = "(%d)"; char tmp[99999]; sprintf(tmp, format.c_str(), size); format = string(tmp); ofs.write((char*)format, sizeof(format)); ofs.write((char*)table.ptr, sizeof(DataType) * size); ofs.close(); } Table(string name) : table(new DataType[Multiplicative&::value]) {
ifstream ifs("./" + name, ios::in | ios::binary);

string format = "(%d)";
char tmp[99999];
sprintf(tmp, format.c_str(), size, rest...);
format = string(tmp);

ifs.read((char*)format, sizeof(format));
ifs.read((char*)table.ptr, sizeof(DataType) * size);

ifs.close();
}

void operator=(Table& c) {
table.ptr = c.table.ptr;
c.table.ptr = nullptr;
}
};

#pragma endregion


不建议这么做。因为 [] [] 是两个独立的操作符,意味著拿[]的返回值再进行一次operator[]操作。理论上可以通过一个代理来实现,一旦这么搞,就必然意味著会有人拿单独的 [] 调用出那个中间代理用于做一些你想像不到的用途,事态可能变得难以控制。

变通的办法是把双下标做成单一调用。

比如说 a[{0,0}] (重载 [公式] 接收一个常数数组作为参数),或者比如说 a(0,0) (重载 [公式] 接收两个数作为参数)。


我觉得如果没有很特殊的需要的话,你完全没必要重载[][]啊,你重载[]然后返回一个double*不就好了


class A{
public:
double mArray[2][2];
double* operator[] (int index){
return mArray[index];
}
};

木考虑越界,方式上可以这样实现(我在自己机器上可以通过)


a[0]返回一个A_类型的值a_,a_[0]再返回最终值


其实就是实现 operator 【】就好了啊


建立一个中间对象


推荐阅读:
相关文章