Est-il possible de surcharger l' []
opérateur deux fois? Pour permettre, quelque chose comme ceci: function[3][3]
(comme dans un tableau à deux dimensions).
Si c'est possible, j'aimerais voir un exemple de code.
Est-il possible de surcharger l' []
opérateur deux fois? Pour permettre, quelque chose comme ceci: function[3][3]
(comme dans un tableau à deux dimensions).
Si c'est possible, j'aimerais voir un exemple de code.
std::vector
avec un constructeur de plage: stackoverflow.com/a/25405865/610351
using array2d = std::array<std::array<int, 3>, 3>;
Réponses:
Vous pouvez surcharger operator[]
pour renvoyer un objet sur lequel vous pouvez utiliser à operator[]
nouveau pour obtenir un résultat.
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;
};
Ensuite, vous pouvez l'utiliser comme:
ArrayOfArrays aoa;
aoa[3][5];
Ceci n'est qu'un exemple simple, vous voudriez ajouter un tas de vérifications de limites et d'autres choses, mais vous voyez l'idée.
Proxy::operator[]
devrait revenir int&
non seulementint
std::vector<std::vector<int>>
pour éviter les memleak et les comportements étranges sur la copie.
multi_array
et extent_gen
sont de bons exemples de cette technique. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
const ArrayOfArrays arr; arr[3][5] = 42;
sera en mesure de passer la compilation et des changements arr[3][5]
, ce qui est quelque peu différente de ce que l' attente des utilisateurs qui arr
est const
.
Proxy::operator[]
ne renvoie pas de référence dans ce code (en supposant que votre commentaire ne soit pas une réponse à Ryan Haining). Plus important encore, si arr
est const operator[]
ne peut pas être utilisé. Vous auriez à définir une version const, et bien sûr vous la feriez revenir const Proxy
. Alors Proxy
lui-même aurait des méthodes const et non-const. Et puis votre exemple ne se compilerait toujours pas, et le programmeur serait heureux que tout va bien dans l'univers. =)
Une expression x[y][z]
nécessite que x[y]
s'évalue à un objet d
qui prend en charge d[z]
.
Cela signifie qu'il x[y]
devrait s'agir d'un objet avec un operator[]
qui s'évalue en un "objet proxy" qui prend également en charge un operator[]
.
C'est la seule façon de les enchaîner.
Vous pouvez également surcharger operator()
pour prendre plusieurs arguments, de sorte que vous puissiez appeler myObject(x,y)
.
Pour un tableau à deux dimensions, en particulier, vous pourriez vous en sortir avec une seule surcharge d'opérateur [] qui renvoie un pointeur vers le premier élément de chaque ligne.
Vous pouvez ensuite utiliser l'opérateur d'indexation intégré pour accéder à chaque élément de la ligne.
Une approche consiste à utiliser 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];
}
Bien sûr, vous pouvez typedef
lepair<int,int>
nValue = theArray[{2,3}];
Vous pouvez utiliser un objet proxy, quelque chose comme ceci:
#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;
}
Ça va être génial si vous pouvez me faire savoir ce que function
, function[x]
et function[x][y]
êtes. Mais de toute façon permettez-moi de le considérer comme un objet déclaré quelque part comme
SomeClass function;
(Parce que vous avez dit que c'est une surcharge d'opérateurs, je pense que vous ne serez pas intéressé par un tableau comme SomeClass function[16][32];
)
Il en function
va de même pour une instance de type SomeClass
. Ensuite, recherchez la déclaration de SomeClass
pour le type de retour de operator[]
surcharge, tout comme
ReturnType operator[](ParamType);
Ensuite, function[x]
aura le type ReturnType
. Encore une fois, recherchez ReturnType
la operator[]
surcharge. S'il existe une telle méthode, vous pouvez alors utiliser l'expression function[x][y]
.
Note, à la différence function(x, y)
, function[x][y]
sont 2 appels distincts. Il est donc difficile pour le compilateur ou le runtime de garantir l'atomicité sauf si vous utilisez un verrou dans le contexte. Un exemple similaire est, libc dit que printf
c'est atomique alors que les appels successifs au operator<<
flux de sortie surchargé ne le sont pas. Une déclaration comme
std::cout << "hello" << std::endl;
peut avoir un problème dans une application multi-thread, mais quelque chose comme
printf("%s%s", "hello", "\n");
c'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];
};
J'ai trouvé ma propre solution simple à cela.
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)};}
Cela vous permet de prendre un lambda et de produire un indexeur (avec []
support).
Supposons que vous ayez un operator()
qui prend en charge le passage des deux coordonnées à onxe comme deux arguments. Maintenant, le [][]
support d' écriture est juste:
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);}
);
}
Et.. Voila. Aucune classe personnalisée requise.
Si, au lieu de dire un [x] [y], vous souhaitez dire un [{x, y}], vous pouvez faire comme ceci:
struct Coordinate { int x, y; }
class Matrix {
int** data;
operator[](Coordinate c) {
return data[c.y][c.x];
}
}
Il est possible de surcharger plusieurs [] en utilisant un gestionnaire de modèle spécialisé. Juste pour montrer comment cela fonctionne:
#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;
}
Et maintenant, la définition de SubscriptHandler<ClassType,ArgType,RetType,N>
pour faire fonctionner le code précédent. Cela montre seulement comment cela peut être fait. Cette solution est optimale ni sans bogue (pas threadsafe par exemple).
#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);
}
};
Avec a std::vector<std::vector<type*>>
, vous pouvez créer le vecteur intérieur à l'aide d'un opérateur d'entrée personnalisé qui itère sur vos données et renvoie un pointeur vers chaque donnée.
Par exemple:
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])));
}
Cette solution a l'avantage de vous fournir un véritable conteneur STL, vous pouvez donc utiliser des boucles for spéciales, des algorithmes 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;
Cependant, il crée des vecteurs de pointeurs, donc si vous utilisez de petites structures de données comme celle-ci, vous pouvez directement copier le contenu à l'intérieur du tableau.
Exemple de code:
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>> ou T ** n'est requis que lorsque vous avez des lignes de longueur variable et bien trop inefficaces en termes d'utilisation de mémoire / d'allocations si vous avez besoin d'un tableau rectangulaire, envisagez de faire des maths à la place! voir la méthode 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; }
};
En utilisant C ++ 11 et la bibliothèque standard, vous pouvez créer un très beau tableau à deux dimensions en une seule ligne de code:
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;
En décidant que la matrice interne représente les lignes, vous accédez à la matrice avec une myMatrix[y][x]
syntaxe:
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();
Et vous pouvez utiliser à distance for
pour la sortie:
for (const auto &row : myMatrix) {
for (const auto &elem : row) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
(Décider des array
colonnes de représentation internes permettrait une foo[x][y]
syntaxe, mais vous devrez utiliser des for(;;)
boucles plus maladroites pour afficher la sortie.)
Mes 5 cents.
Je savais intuitivement que je devais faire beaucoup de code passe-partout.
C'est pourquoi, au lieu de operator [], j'ai fait un opérateur surchargé (int, int). Puis en résultat final, au lieu de m [1] [2], j'ai fait m (1,2)
Je sais que c'est différent, mais c'est toujours très intuitif et ressemble à un script mathématique.
La solution la plus courte et la plus simple:
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)
place ...