È possibile sovraccaricare l' []operatore due volte? Per consentire, qualcosa del genere: function[3][3](come in un array bidimensionale).
Se è possibile, vorrei vedere un codice di esempio.
È possibile sovraccaricare l' []operatore due volte? Per consentire, qualcosa del genere: function[3][3](come in un array bidimensionale).
Se è possibile, vorrei vedere un codice di esempio.
std::vectorcon un costruttore di intervalli: stackoverflow.com/a/25405865/610351
using array2d = std::array<std::array<int, 3>, 3>;
Risposte:
È possibile eseguire l'overload operator[]per restituire un oggetto su cui è possibile utilizzare operator[]nuovamente per ottenere un risultato.
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;
};
Quindi puoi usarlo come:
ArrayOfArrays aoa;
aoa[3][5];
Questo è solo un semplice esempio, vorresti aggiungere un po 'di controllo dei limiti e cose del genere, ma hai capito.
Proxy::operator[]dovrebbe tornare int&non soloint
std::vector<std::vector<int>>per evitare memleak e strani comportamenti sulla copia.
multi_arrayche extent_gensono buoni esempi di questa tecnica. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
const ArrayOfArrays arr; arr[3][5] = 42;sarà in grado di passare la compilazione e modifiche arr[3][5], che è in qualche modo diverso da quello che le aspettative degli utenti che arrè const.
Proxy::operator[]non restituisce un riferimento in questo codice (supponendo che il tuo commento non sia in risposta a Ryan Haining). Ancora più importante, se arrè const, operator[]non può essere utilizzato. Dovresti definire una versione const e ovviamente la faresti tornare const Proxy. Quindi esso Proxystesso avrebbe metodi const e non const. E poi il tuo esempio non si compilerebbe ancora e il programmatore sarebbe felice che tutto vada bene nell'universo. =)
Un'espressione x[y][z]richiede che x[y]restituisca un oggetto dche supporta d[z].
Ciò significa che x[y]dovrebbe essere un oggetto con un operator[]che restituisce un "oggetto proxy" che supporta anche un operator[].
Questo è l'unico modo per incatenarli.
In alternativa, operator()eseguire l' overload per accettare più argomenti, in modo da poter richiamare myObject(x,y).
Per un array bidimensionale, in particolare, potresti farla franca con un singolo overload dell'operatore [] che restituisce un puntatore al primo elemento di ogni riga.
Quindi è possibile utilizzare l'operatore di indicizzazione incorporato per accedere a ogni elemento all'interno della riga.
Un approccio sta usando 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];
}
Certo, puoi typedefilpair<int,int>
nValue = theArray[{2,3}];
Puoi usare un oggetto proxy, qualcosa del genere:
#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;
}
Sarebbe fantastico se mi facessi sapere cosa function, function[x]e function[x][y]sono. Ma in ogni caso fammi considerare come un oggetto dichiarato da qualche parte come
SomeClass function;
(Poiché hai detto che è un sovraccarico di operatori, penso che non ti interesserà l'array come SomeClass function[16][32];)
Quindi functionè un'istanza di tipo SomeClass. Quindi cerca la dichiarazione di SomeClassper il tipo restituito di operator[]overload, proprio come
ReturnType operator[](ParamType);
Quindi function[x]avrà il tipo ReturnType. Cerca di nuovo ReturnTypeil operator[]sovraccarico. Se esiste un tale metodo, puoi usare l'espressione function[x][y].
Nota, a differenza function(x, y), function[x][y]sono 2 chiamate separate. Quindi è difficile per il compilatore o il runtime garantisce l'atomicità a meno che non si utilizzi un blocco nel contesto. Un esempio simile è che libc dice che printfè atomico mentre le chiamate successive al operator<<flusso di output sovraccaricato non lo sono. Una dichiarazione come
std::cout << "hello" << std::endl;
potrebbe avere problemi nell'applicazione multi-thread, ma qualcosa di simile
printf("%s%s", "hello", "\n");
è ok.
#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];
};
Ho trovato la mia semplice soluzione a questo.
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)};}
Ciò ti consente di prendere un lambda e produrre un indicizzatore (con []supporto).
Supponi di avere un operator()che supporta il passaggio di entrambe le coordinate su onxe come due argomenti. Ora il [][]supporto per la scrittura è 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);}
);
}
E fatto. Nessun corso personalizzato richiesto.
Se, invece di dire a [x] [y], vuoi dire a [{x, y}], puoi fare così:
struct Coordinate { int x, y; }
class Matrix {
int** data;
operator[](Coordinate c) {
return data[c.y][c.x];
}
}
È possibile eseguire l'overload di più [] utilizzando un gestore di modelli specializzato. Solo per mostrare come funziona:
#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;
}
E ora la definizione di SubscriptHandler<ClassType,ArgType,RetType,N>per far funzionare il codice precedente. Mostra solo come si può fare. Questa soluzione è ottimale né priva di bug (non sicura per i thread, ad esempio).
#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*>>, puoi costruire il vettore interno usando un operatore di input personalizzato che itera sui tuoi dati e restituisce un puntatore a ogni dato.
Per esempio:
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])));
}
Questa soluzione ha il vantaggio di fornirti un vero contenitore STL, quindi puoi usare speciali per cicli, algoritmi STL e così via.
for (size_t i = 0; i < w; ++i)
for (size_t j = 0; j < h; ++j)
std::cout << *data[i][j] << std::endl;
Tuttavia, crea vettori di puntatori, quindi se stai usando piccole strutture dati come questa puoi copiare direttamente il contenuto all'interno dell'array.
Codice di esempio:
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 ** è richiesto solo quando hai righe di lunghezza variabile e troppo inefficienti in termini di utilizzo / allocazioni della memoria se hai bisogno di un array rettangolare, considera invece di fare un po 'di matematica! vedi il metodo ():
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; }
};
Usando C ++ 11 e la libreria standard puoi creare un array bidimensionale molto carino in una singola riga di codice:
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;
Decidendo che la matrice interna rappresenta le righe, si accede alla matrice con una myMatrix[y][x]sintassi:
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();
E puoi usare a intervalli forper l'output:
for (const auto &row : myMatrix) {
for (const auto &elem : row) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
(Decidere le arraycolonne delle rappresentazioni interne consentirebbe una foo[x][y]sintassi, ma sarebbe necessario utilizzare for(;;)cicli più goffi per visualizzare l'output.)
I miei 5 centesimi.
Sapevo intuitivamente che dovevo fare un sacco di codice boilerplate.
Questo è il motivo per cui, invece dell'operatore [], ho eseguito l'overload dell'operatore (int, int). Poi nel risultato finale, invece di m [1] [2], ho fatto m (1,2)
So che è DIVERSA cosa, ma è ancora molto intuitivo e sembra una scrittura matematica.
La soluzione più breve e più semplice:
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)invece ...