연산자 [] [] 오버로드


93

[]연산자를 두 번 오버로드 할 수 있습니까? 허용하려면 다음과 같이하십시오. function[3][3](2 차원 배열에서와 같이).

가능하다면 몇 가지 예제 코드를보고 싶습니다.


23
Btw, operator()(int, int)대신 과부하가 훨씬 더 간단하고 일반적입니다 ...
Inverse

2
휠을 다시 만드는 이유는 무엇입니까? std::vector범위 생성자와 함께 사용하십시오 . stackoverflow.com/a/25405865/610351
Geoffroy

또는 다음과 같이 사용할 수 있습니다using array2d = std::array<std::array<int, 3>, 3>;
adembudak

답변:


121

결과를 얻기 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];

이것은 단순한 예에 불과합니다. 경계 검사 및 항목을 추가하고 싶지만 아이디어를 얻었습니다.


5
소멸자를 사용할 수 있습니다. 그리고 Proxy::operator[]반환해야 int&그냥int
라이언 이닝에게

1
std::vector<std::vector<int>>복사시 멤 리크 및 이상한 동작을 피 하려면 사용하는 것이 좋습니다.
Jarod42 2014-08-20

Boost multi_array와 모두이 extent_gen기술의 좋은 예입니다. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
alfC

1
그러나 사용자의 기대와 는 다른 const ArrayOfArrays arr; arr[3][5] = 42;컴파일 및 변경 사항을 전달할 수 있습니다 . arr[3][5]arrconst
abcdabcd987 2015

5
@ abcdabcd987 몇 가지 이유로 올바르지 않습니다. 첫째, Proxy::operator[]이 코드에서 참조를 반환하지 않습니다 (귀하의 의견이 Ryan Haining에 대한 응답이 아니라고 가정). 더 중요한 것은 if arr가 const이면 operator[]사용할 수 없다는 것입니다. const 버전을 정의해야하고 물론 const Proxy. 그러면 Proxy자체적으로 const 및 non-const 메서드가 있습니다. 그리고 당신의 예제는 여전히 컴파일되지 않을 것이고 프로그래머는 우주에서 모든 것이 잘되고 좋다고 기뻐할 것입니다. =)
paddy

21

을 지원 하는 객체로 평가 되는 표현식이 x[y][z]필요 합니다 .x[y]dd[z]

이 수단 x[y]가진 객체되어야 operator[]는 "프록시 객체"에 해당 평가하여 을 지원한다 operator[].

이것이 그들을 연결하는 유일한 방법입니다.

또는을 operator()호출 할 수 있도록 오버로드 하여 여러 인수를 가져옵니다 myObject(x,y).


괄호의 과부하로 인해 두 개의 입력이 허용되지만 괄호로 똑같이 할 수없는 이유는 무엇입니까?
A. Frenzy

20

특히 2 차원 배열의 경우 각 행의 첫 번째 요소에 대한 포인터를 반환하는 단일 operator [] 오버로드를 사용할 수 있습니다.

그런 다음 내장 인덱싱 연산자를 사용하여 행 내의 각 요소에 액세스 할 수 있습니다.


4
나에게 가장 실용적이고 효율적인 솔루션처럼 보입니다. 왜 더 많은 표를 얻지 못하는지 궁금합니다. 아마도 눈길을 끄는 코드가 없기 때문일 것입니다.
Yigal Reiss

16

첫 번째 [] 호출에서 어떤 종류의 프록시 클래스를 반환하면 가능합니다. 그러나 다른 옵션이 있습니다. 임의의 수의 인수 ( function(3,3))를 허용 할 수있는 operator ()를 오버로드 할 수 있습니다 .


9

한 가지 접근 방식은 다음을 사용하는 것입니다 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];
}

물론, 당신은 할 수있다typedefpair<int,int>


8
이것은 C ++ 11 및 중괄호 초기화 로 훨씬 더 매력적입니다. 지금 당신은 쓸 수 있습니다nValue = theArray[{2,3}];
마틴 보너 모니카 지원

5

다음과 같은 프록시 객체를 사용할 수 있습니다.

#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;
}

4

그것은 '당신은 내가 무엇을 알릴 수 있다면 좋을 것이다 function, function[x]그리고function[x][y] 있습니다. 그러나 어쨌든 어딘가에 선언 된 객체로 간주하겠습니다.

SomeClass function;

(연산자 과부하라고 말했기 때문에 배열에 관심이 없을 것 같습니다. 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]이 별도의 전화입니다. 따라서 컨텍스트에서 잠금을 사용하지 않는 한 컴파일러 또는 런타임이 원 자성을 보장하기가 어렵습니다. 유사한 예는 libc printf가 원자 적이 라고 말하지만 operator<<출력 스트림 의 오버로드 에 대한 연속 호출 은 그렇지 않습니다. 다음과 같은 진술

std::cout << "hello" << std::endl;

다중 스레드 응용 프로그램에서 문제가있을 수 있지만

printf("%s%s", "hello", "\n");

괜찮습니다.


2
#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;
}

2
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];
};

이것에 대한 내 자신의 간단한 해결책을 찾았습니다.


2
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);}
  );
}

그리고 끝났습니다. 맞춤 클래스가 필요하지 않습니다.


2

a [x] [y] 대신 a [{x, y}]라고 말하고 싶다면 다음과 같이 할 수 있습니다.

struct Coordinate {  int x, y; }

class Matrix {
    int** data;
    operator[](Coordinate c) {
        return data[c.y][c.x];
    }
}

1

특수 템플릿 처리기를 사용하여 여러 []를 오버로드 할 수 있습니다. 어떻게 작동하는지 보여주기 위해 :

#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);
    }
};

0

를 사용하면 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;

그러나 포인터 벡터를 생성하므로 이와 같은 작은 데이터 구조를 사용하는 경우 배열 내부의 콘텐츠를 직접 복사 할 수 있습니다.


0

샘플 코드 :

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;
}

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; }
};

0

C ++ 11과 표준 라이브러리를 사용하면 한 줄의 코드로 매우 멋진 2 차원 배열을 만들 수 있습니다.

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출력을 위해 ranged- 를 사용할 수 있습니다 .

for (const auto &row : myMatrix) {
  for (const auto &elem : row) {
    std::cout << elem << " ";
  }
  std::cout << std::endl;
}

(내부 array표시 열을 결정하면 foo[x][y]구문 이 허용 되지만 for(;;)출력을 표시 하려면 clumsier 루프를 사용해야 합니다.)


0

내 5 센트.

저는 많은 상용구 코드를 작성해야한다는 것을 직관적으로 알고있었습니다.

이것이 내가 연산자 [] 대신에 연산자 (int, int)를 오버로드 한 이유입니다. 그런 다음 최종 결과에서 m [1] [2] 대신 m (1,2)를 수행했습니다.

나는 그것이 다른 것임을 알고 있지만 여전히 매우 직관적이며 수학 스크립트처럼 보입니다.


0

가장 짧고 쉬운 솔루션 :

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];
  }

};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.