Можно ли []
дважды перегрузить оператора? Чтобы разрешить, что-то вроде этого: function[3][3]
(как в двумерном массиве).
Если возможно, я хотел бы увидеть пример кода.
c++
operator-overloading
Icepopo
источник
источник
operator()(int, int)
...std::vector
с конструктором диапазона: stackoverflow.com/a/25405865/610351using array2d = std::array<std::array<int, 3>, 3>;
Ответы:
Вы можете перегрузить,
operator[]
чтобы вернуть объект, который можноoperator[]
снова использовать для получения результата.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; };
Тогда вы можете использовать это как:
ArrayOfArrays aoa; aoa[3][5];
Это всего лишь простой пример, вы бы хотели добавить кучу проверки границ и прочего, но вы поняли идею.
источник
Proxy::operator[]
должен вернутьсяint&
не просто такint
std::vector<std::vector<int>>
чтобы избежать утечки памяти и странного поведения при копировании.multi_array
иextent_gen
являются хорошими примерами этой техники. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…const ArrayOfArrays arr; arr[3][5] = 42;
будет иметь возможность пройти компиляцию и измененияarr[3][5]
, которые каким - то образом отличается от того , ожидания пользователей , чтоarr
естьconst
.Proxy::operator[]
не возвращает ссылку в этом коде (при условии, что ваш комментарий не является ответом Райану Хейнингу). Что еще более важно, еслиarr
const, тоoperator[]
использовать нельзя. Вам нужно будет определить константную версию и, конечно, вы должны вернуть ееconst Proxy
. Тогда уProxy
себя были бы методы const и неконстантные. И тогда ваш пример все равно не компилируется, и программист будет счастлив, что во вселенной все хорошо. =)Выражение
x[y][z]
требует, чтобы результатx[y]
оценивался какd
поддерживающий объектd[z]
.Это означает, что это
x[y]
должен быть объект с расширением,operator[]
который оценивается как «прокси-объект», который также поддерживаетoperator[]
.Это единственный способ связать их.
В качестве альтернативы перегрузите,
operator()
чтобы принять несколько аргументов, чтобы вы могли вызватьmyObject(x,y)
.источник
В частности, для двумерного массива можно обойтись одной перегрузкой operator [], которая возвращает указатель на первый элемент каждой строки.
Затем вы можете использовать встроенный оператор индексации для доступа к каждому элементу в строке.
источник
Это возможно, если вы вернете какой-то прокси-класс в вызове first []. Однако есть и другой вариант: вы можете перегрузить operator (), который может принимать любое количество аргументов (
function(3,3)
).источник
Один из подходов использует
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]; }
Конечно, вы можете
typedef
pair<int,int>
источник
nValue = theArray[{2,3}];
Вы можете использовать прокси-объект, например:
#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; }
источник
Это будет замечательно , если вы можете , дайте мне знать , что
function
,function[x]
иfunction[x][y]
есть. Но в любом случае позвольте мне рассматривать это как объект, объявленный где-то вроде(Поскольку вы сказали, что это перегрузка оператора, я думаю, вам не будет интересен массив вроде
SomeClass function[16][32];
)Так
function
что это экземпляр типаSomeClass
. Затем найдите объявлениеSomeClass
для возвращаемого типаoperator[]
перегрузки, как иReturnType operator[](ParamType);
Тогда
function[x]
будет типReturnType
. Опять же искатьReturnType
дляoperator[]
перегрузки. Если есть такой метод, вы можете использовать выражениеfunction[x][y]
.Обратите внимание, в отличие от этого
function(x, y)
,function[x][y]
это 2 отдельных вызова. Таким образом, компилятору или среде выполнения сложно гарантировать атомарность, если вы не используете блокировку в контексте. Похожий пример: libc утверждает, чтоprintf
это атомарно, а последовательные вызовы перегруженногоoperator<<
выходного потока - нет. Заявление вродеstd::cout << "hello" << std::endl;
может быть проблема в многопоточном приложении, но что-то вроде
printf("%s%s", "hello", "\n");
Это хорошо.
источник
#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]; };
Нашел собственное простое решение этой проблемы.
источник
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)};}
Это позволяет вам взять лямбду и создать индексатор (с
[]
поддержкой).Предположим, у вас есть объект,
operator()
который поддерживает передачу обеих координат в onxe в качестве двух аргументов. Теперь написание[][]
поддержки - это просто: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);} ); }
И готово. Не требует специального класса.
источник
Если вместо [x] [y] вы хотите сказать [{x, y}], вы можете сделать следующее:
struct Coordinate { int x, y; } class Matrix { int** data; operator[](Coordinate c) { return data[c.y][c.x]; } }
источник
Можно перегрузить несколько [], используя специализированный обработчик шаблонов. Просто чтобы показать, как это работает:
#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; }
А теперь определение,
SubscriptHandler<ClassType,ArgType,RetType,N>
как заставить работать предыдущий код. Это только показывает, как это можно сделать. Это решение является оптимальным и не лишенным ошибок (например, небезопасным для потоков).#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); } };
источник
С помощью a
std::vector<std::vector<type*>>
вы можете построить внутренний вектор, используя пользовательский оператор ввода, который выполняет итерацию по вашим данным и возвращает указатель на все данные.Например:
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]))); }
Живой пример
Это решение имеет то преимущество, что предоставляет вам настоящий контейнер STL, поэтому вы можете использовать специальные циклы for, алгоритмы STL и так далее.
for (size_t i = 0; i < w; ++i) for (size_t j = 0; j < h; ++j) std::cout << *data[i][j] << std::endl;
Однако он создает векторы указателей, поэтому, если вы используете небольшие структуры данных, такие как эта, вы можете напрямую копировать содержимое внутри массива.
источник
Образец кода:
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>> или T ** требуется только тогда, когда у вас есть строки переменной длины и они слишком неэффективны с точки зрения использования / выделения памяти, если вам нужен прямоугольный массив, подумайте о том, чтобы вместо этого выполнить некоторую математику! см. метод 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; } };
источник
Используя C ++ 11 и Стандартную библиотеку, вы можете создать очень красивый двумерный массив в одной строке кода:
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;
Решив, что внутренняя матрица представляет строки, вы получаете доступ к матрице с помощью
myMatrix[y][x]
синтаксиса: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();
И вы можете использовать ранжированный
for
для вывода:for (const auto &row : myMatrix) { for (const auto &elem : row) { std::cout << elem << " "; } std::cout << std::endl; }
(Решение о
array
столбцах внутреннего представления позволило бы использоватьfoo[x][y]
синтаксис, ноfor(;;)
для отображения вывода нужно использовать более неуклюжие циклы.)источник
Мои 5 центов.
Я интуитивно знал, что мне нужно сделать много шаблонного кода.
Вот почему вместо operator [] я использовал перегруженный оператор (int, int). Тогда в конечном результате вместо m [1] [2] я сделал m (1,2)
Я знаю, что это ДРУГАЯ вещь, но все еще очень интуитивно понятна и выглядит как математический сценарий.
источник
Самое короткое и простое решение:
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]; } };
источник