¿Es posible sobrecargar al []operador dos veces? Para permitir, algo como esto: function[3][3](como en una matriz bidimensional).
Si es posible, me gustaría ver algún código de ejemplo.
¿Es posible sobrecargar al []operador dos veces? Para permitir, algo como esto: function[3][3](como en una matriz bidimensional).
Si es posible, me gustaría ver algún código de ejemplo.
std::vectorcon un constructor de rango: stackoverflow.com/a/25405865/610351
using array2d = std::array<std::array<int, 3>, 3>;
Respuestas:
Puede sobrecargar operator[]para devolver un objeto que puede utilizar de operator[]nuevo para obtener un resultado.
class ArrayOfArrays {
public:
ArrayOfArrays() {
_arrayofarrays = new int*[10];
for(int i = 0; i < 10; ++i)
_arrayofarrays[i] = new int[10];
}
class Proxy {
public:
Proxy(int* _array) : _array(_array) { }
int operator[](int index) {
return _array[index];
}
private:
int* _array;
};
Proxy operator[](int index) {
return Proxy(_arrayofarrays[index]);
}
private:
int** _arrayofarrays;
};
Entonces puedes usarlo como:
ArrayOfArrays aoa;
aoa[3][5];
Este es solo un ejemplo simple, querría agregar un montón de verificación de límites y esas cosas, pero entiendes la idea.
Proxy::operator[]debería regresar int&no soloint
std::vector<std::vector<int>>para evitar fugas de memoria y comportamientos extraños en la copia.
multi_arraycomo extent_genson buenos ejemplos de esta técnica. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
const ArrayOfArrays arr; arr[3][5] = 42;será capaz de pasar la compilación y cambios arr[3][5], que es algo diferente de lo que las expectativas de los usuarios que arres const.
Proxy::operator[]no devuelve una referencia en este código (asumiendo que su comentario no es una respuesta a Ryan Haining). Más importante aún, si arres constante, entonces operator[]no se puede usar. Tendría que definir una versión constante y, por supuesto, la haría regresar const Proxy. Entonces Proxysí mismo tendría métodos const y no const. Y entonces su ejemplo aún no se compilaría, y el programador estaría feliz de que todo esté bien en el universo. =)
Una expresión x[y][z]requiere que se x[y]evalúe como un objeto dque admita d[z].
Esto significa que x[y]debe ser un objeto con un operator[]que se evalúe como un "objeto proxy" que también admita un operator[].
Esta es la única forma de encadenarlos.
Alternativamente, sobrecargue operator()para tomar múltiples argumentos, de modo que pueda invocar myObject(x,y).
Para una matriz bidimensional, específicamente, puede salirse con la suya con una sobrecarga de operador [] que devuelve un puntero al primer elemento de cada fila.
Luego, puede usar el operador de indexación incorporado para acceder a cada elemento dentro de la fila.
Un enfoque es utilizar std::pair<int,int>:
class Array2D
{
int** m_p2dArray;
public:
int operator[](const std::pair<int,int>& Index)
{
return m_p2dArray[Index.first][Index.second];
}
};
int main()
{
Array2D theArray;
pair<int, int> theIndex(2,3);
int nValue;
nValue = theArray[theIndex];
}
Por supuesto, es posible que typedefelpair<int,int>
nValue = theArray[{2,3}];
Puede usar un objeto proxy, algo como esto:
#include <iostream>
struct Object
{
struct Proxy
{
Object *mObj;
int mI;
Proxy(Object *obj, int i)
: mObj(obj), mI(i)
{
}
int operator[](int j)
{
return mI * j;
}
};
Proxy operator[](int i)
{
return Proxy(this, i);
}
};
int main()
{
Object o;
std::cout << o[2][3] << std::endl;
}
Es ll ser bueno si se puede me informara function, function[x]y function[x][y]está. Pero de todos modos déjame considerarlo como un objeto declarado en algún lugar como
SomeClass function;
(Como dijiste que es una sobrecarga del operador, creo que no te interesará una matriz como SomeClass function[16][32];)
También lo functiones una instancia de tipo SomeClass. Luego busque la declaración de SomeClasspara el tipo de retorno de operator[]sobrecarga, como
ReturnType operator[](ParamType);
Entonces function[x]tendrá el tipo ReturnType. Vuelva a buscar ReturnTypela operator[]sobrecarga. Si existe tal método, puede usar la expresión function[x][y].
Tenga en cuenta, a diferencia de function(x, y), function[x][y]son 2 llamadas separadas. Por lo tanto, es difícil para el compilador o el tiempo de ejecución garantiza la atomicidad a menos que use un bloqueo en el contexto. Un ejemplo similar es, libc dice que printfes atómico, mientras que las llamadas sucesivas al operator<<flujo de salida sobrecargado no lo son. Una declaración como
std::cout << "hello" << std::endl;
podría tener un problema en la aplicación de subprocesos múltiples, pero algo como
printf("%s%s", "hello", "\n");
está bien.
#include<iostream>
using namespace std;
class Array
{
private: int *p;
public:
int length;
Array(int size = 0): length(size)
{
p=new int(length);
}
int& operator [](const int k)
{
return p[k];
}
};
class Matrix
{
private: Array *p;
public:
int r,c;
Matrix(int i=0, int j=0):r(i), c(j)
{
p= new Array[r];
}
Array& operator [](const int& i)
{
return p[i];
}
};
/*Driver program*/
int main()
{
Matrix M1(3,3); /*for checking purpose*/
M1[2][2]=5;
}
struct test
{
using array_reference = int(&)[32][32];
array_reference operator [] (std::size_t index)
{
return m_data[index];
}
private:
int m_data[32][32][32];
};
Encontré mi propia solución simple para esto.
template<class F>
struct indexer_t{
F f;
template<class I>
std::result_of_t<F const&(I)> operator[](I&&i)const{
return f(std::forward<I>(i))1;
}
};
template<class F>
indexer_t<std::decay_t<F>> as_indexer(F&& f){return {std::forward<F>(f)};}
Esto le permite tomar una lambda y producir un indexador (con []soporte).
Suponga que tiene un operator()que admite pasar ambas coordenadas en onxe como dos argumentos. Ahora el [][]soporte de escritura es solo:
auto operator[](size_t i){
return as_indexer(
[i,this](size_t j)->decltype(auto)
{return (*this)(i,j);}
);
}
auto operator[](size_t i)const{
return as_indexer(
[i,this](size_t j)->decltype(auto)
{return (*this)(i,j);}
);
}
Y hecho. No se requiere clase personalizada.
Si, en lugar de decir una [x] [y], le gustaría decir una [{x, y}], puede hacer lo siguiente:
struct Coordinate { int x, y; }
class Matrix {
int** data;
operator[](Coordinate c) {
return data[c.y][c.x];
}
}
Es posible sobrecargar múltiples [] usando un manejador de plantillas especializado. Solo para mostrar cómo funciona:
#include <iostream>
#include <algorithm>
#include <numeric>
#include <tuple>
#include <array>
using namespace std;
// the number '3' is the number of [] to overload (fixed at compile time)
struct TestClass : public SubscriptHandler<TestClass,int,int,3> {
// the arguments will be packed in reverse order into a std::array of size 3
// and the last [] will forward them to callSubscript()
int callSubscript(array<int,3>& v) {
return accumulate(v.begin(),v.end(),0);
}
};
int main() {
TestClass a;
cout<<a[3][2][9]; // prints 14 (3+2+9)
return 0;
}
Y ahora la definición de SubscriptHandler<ClassType,ArgType,RetType,N>hacer que el código anterior funcione. Solo muestra cómo se puede hacer. Esta solución es óptima y no está libre de errores (no segura para subprocesos, por ejemplo).
#include <iostream>
#include <algorithm>
#include <numeric>
#include <tuple>
#include <array>
using namespace std;
template <typename ClassType,typename ArgType,typename RetType, int N> class SubscriptHandler;
template<typename ClassType,typename ArgType,typename RetType, int N,int Recursion> class SubscriptHandler_ {
ClassType*obj;
array<ArgType,N+1> *arr;
typedef SubscriptHandler_<ClassType,ArgType,RetType,N,Recursion-1> Subtype;
friend class SubscriptHandler_<ClassType,ArgType,RetType,N,Recursion+1>;
friend class SubscriptHandler<ClassType,ArgType,RetType,N+1>;
public:
Subtype operator[](const ArgType& arg){
Subtype s;
s.obj = obj;
s.arr = arr;
arr->at(Recursion)=arg;
return s;
}
};
template<typename ClassType,typename ArgType,typename RetType,int N> class SubscriptHandler_<ClassType,ArgType,RetType,N,0> {
ClassType*obj;
array<ArgType,N+1> *arr;
friend class SubscriptHandler_<ClassType,ArgType,RetType,N,1>;
friend class SubscriptHandler<ClassType,ArgType,RetType,N+1>;
public:
RetType operator[](const ArgType& arg){
arr->at(0) = arg;
return obj->callSubscript(*arr);
}
};
template<typename ClassType,typename ArgType,typename RetType, int N> class SubscriptHandler{
array<ArgType,N> arr;
ClassType*ptr;
typedef SubscriptHandler_<ClassType,ArgType,RetType,N-1,N-2> Subtype;
protected:
SubscriptHandler() {
ptr=(ClassType*)this;
}
public:
Subtype operator[](const ArgType& arg){
Subtype s;
s.arr=&arr;
s.obj=ptr;
s.arr->at(N-1)=arg;
return s;
}
};
template<typename ClassType,typename ArgType,typename RetType> struct SubscriptHandler<ClassType,ArgType,RetType,1>{
RetType operator[](const ArgType&arg) {
array<ArgType,1> arr;
arr.at(0)=arg;
return ((ClassType*)this)->callSubscript(arr);
}
};
Con a std::vector<std::vector<type*>>, puede construir el vector interior usando un operador de entrada personalizado que itera sobre sus datos y devuelve un puntero a cada dato.
Por ejemplo:
size_t w, h;
int* myData = retrieveData(&w, &h);
std::vector<std::vector<int*> > data;
data.reserve(w);
template<typename T>
struct myIterator : public std::iterator<std::input_iterator_tag, T*>
{
myIterator(T* data) :
_data(data)
{}
T* _data;
bool operator==(const myIterator& rhs){return rhs.data == data;}
bool operator!=(const myIterator& rhs){return rhs.data != data;}
T* operator*(){return data;}
T* operator->(){return data;}
myIterator& operator++(){data = &data[1]; return *this; }
};
for (size_t i = 0; i < w; ++i)
{
data.push_back(std::vector<int*>(myIterator<int>(&myData[i * h]),
myIterator<int>(&myData[(i + 1) * h])));
}
Esta solución tiene la ventaja de proporcionarle un contenedor STL real, por lo que puede utilizar bucles especiales para, algoritmos STL, etc.
for (size_t i = 0; i < w; ++i)
for (size_t j = 0; j < h; ++j)
std::cout << *data[i][j] << std::endl;
Sin embargo, crea vectores de punteros, por lo que si está utilizando estructuras de datos pequeñas como esta, puede copiar directamente el contenido dentro de la matriz.
Código de muestra:
template<class T>
class Array2D
{
public:
Array2D(int a, int b)
{
num1 = (T**)new int [a*sizeof(int*)];
for(int i = 0; i < a; i++)
num1[i] = new int [b*sizeof(int)];
for (int i = 0; i < a; i++) {
for (int j = 0; j < b; j++) {
num1[i][j] = i*j;
}
}
}
class Array1D
{
public:
Array1D(int* a):temp(a) {}
T& operator[](int a)
{
return temp[a];
}
T* temp;
};
T** num1;
Array1D operator[] (int a)
{
return Array1D(num1[a]);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Array2D<int> arr(20, 30);
std::cout << arr[2][3];
getchar();
return 0;
}
vector <vector <T>> o T ** se requiere solo cuando tiene filas de longitud variable y es demasiado ineficiente en términos de uso de memoria / asignaciones si necesita una matriz rectangular, ¡considere hacer algunas matemáticas en su lugar! ver método at ():
template<typename T > class array2d {
protected:
std::vector< T > _dataStore;
size_t _sx;
public:
array2d(size_t sx, size_t sy = 1): _sx(sx), _dataStore(sx*sy) {}
T& at( size_t x, size_t y ) { return _dataStore[ x+y*sx]; }
const T& at( size_t x, size_t y ) const { return _dataStore[ x+y*sx]; }
const T& get( size_t x, size_t y ) const { return at(x,y); }
void set( size_t x, size_t y, const T& newValue ) { at(x,y) = newValue; }
};
Con C ++ 11 y la biblioteca estándar, puede hacer una matriz bidimensional muy agradable en una sola línea de código:
std::array<std::array<int, columnCount>, rowCount> myMatrix {0};
std::array<std::array<std::string, columnCount>, rowCount> myStringMatrix;
std::array<std::array<Widget, columnCount>, rowCount> myWidgetMatrix;
Al decidir que la matriz interna representa filas, accede a la matriz con una myMatrix[y][x]sintaxis:
myMatrix[0][0] = 1;
myMatrix[0][3] = 2;
myMatrix[3][4] = 3;
std::cout << myMatrix[3][4]; // outputs 3
myStringMatrix[2][4] = "foo";
myWidgetMatrix[1][5].doTheStuff();
Y puede usar ranged- forpara salida:
for (const auto &row : myMatrix) {
for (const auto &elem : row) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
(Decidir las arraycolumnas de representación interna permitiría una foo[x][y]sintaxis, pero necesitaría usar for(;;)bucles más torpes para mostrar la salida).
Mis 5 centavos.
Intuitivamente sabía que necesitaba hacer mucho código repetitivo.
Es por eso que, en lugar de operator [], hice operador sobrecargado (int, int). Luego, en el resultado final, en lugar de m [1] [2], hice m (1,2)
Sé que es algo DIFERENTE, pero sigue siendo muy intuitivo y parece un guión matemático.
La solución más rápida y sencilla:
class Matrix
{
public:
float m_matrix[4][4];
// for statements like matrix[0][0] = 1;
float* operator [] (int index)
{
return m_matrix[index];
}
// for statements like matrix[0][0] = otherMatrix[0][0];
const float* operator [] (int index) const
{
return m_matrix[index];
}
};
operator()(int, int)lugar ...