¿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.
c++
operator-overloading
helado
fuente
fuente
operator()(int, int)
lugar ...std::vector
con un constructor de rango: stackoverflow.com/a/25405865/610351using array2d = std::array<std::array<int, 3>, 3>;
Respuestas:
Puede sobrecargar
operator[]
para devolver un objeto que puede utilizar deoperator[]
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.
fuente
Proxy::operator[]
debería regresarint&
no soloint
std::vector<std::vector<int>>
para evitar fugas de memoria y comportamientos extraños en la copia.multi_array
comoextent_gen
son 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 cambiosarr[3][5]
, que es algo diferente de lo que las expectativas de los usuarios quearr
esconst
.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, siarr
es constante, entoncesoperator[]
no se puede usar. Tendría que definir una versión constante y, por supuesto, la haría regresarconst Proxy
. EntoncesProxy
sí 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 sex[y]
evalúe como un objetod
que admitad[z]
.Esto significa que
x[y]
debe ser un objeto con unoperator[]
que se evalúe como un "objeto proxy" que también admita unoperator[]
.Esta es la única forma de encadenarlos.
Alternativamente, sobrecargue
operator()
para tomar múltiples argumentos, de modo que pueda invocarmyObject(x,y)
.fuente
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.
fuente
Es posible si devuelve algún tipo de clase de proxy en la primera llamada []. Sin embargo, hay otra opción: puede sobrecargar el operador () que puede aceptar cualquier número de argumentos (
function(3,3)
).fuente
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
typedef
elpair<int,int>
fuente
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; }
fuente
Es ll ser bueno si se puede me informara
function
,function[x]
yfunction[x][y]
está. Pero de todos modos déjame considerarlo como un objeto declarado en algún lugar como(Como dijiste que es una sobrecarga del operador, creo que no te interesará una matriz como
SomeClass function[16][32];
)También lo
function
es una instancia de tipoSomeClass
. Luego busque la declaración deSomeClass
para el tipo de retorno deoperator[]
sobrecarga, comoReturnType operator[](ParamType);
Entonces
function[x]
tendrá el tipoReturnType
. Vuelva a buscarReturnType
laoperator[]
sobrecarga. Si existe tal método, puede usar la expresiónfunction[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 queprintf
es atómico, mientras que las llamadas sucesivas aloperator<<
flujo de salida sobrecargado no lo son. Una declaración comostd::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.
fuente
#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; }
fuente
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.
fuente
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.
fuente
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]; } }
fuente
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); } };
fuente
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]))); }
Ejemplo vivo
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.
fuente
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; }
fuente
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; } };
fuente
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-
for
para salida:for (const auto &row : myMatrix) { for (const auto &elem : row) { std::cout << elem << " "; } std::cout << std::endl; }
(Decidir las
array
columnas de representación interna permitiría unafoo[x][y]
sintaxis, pero necesitaría usarfor(;;)
bucles más torpes para mostrar la salida).fuente
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.
fuente
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]; } };
fuente