Kilka nawiasów kwadratowych

2008-09-12 9:58

Gdy w C++ tworzymy typ wymagający indeksowania więcej niż jednym indeksem – a więc coś w stylu wielowymiarowej tablicy, np. macierzy – zazwyczaj używa się do tego celu operatora nawiasów okrągłych. Nie jest to specjalnie spójne z tablicami wbudowanymi język, gdzie do indeksowania stosuje się nawiasy kwadratowe, w tym przypadku nawet więcej niż jedną parę.
O ile jednak da się przeciążyć operator [], o tyle “operatorów” [][], [][][], itd. już nie. Można jednak zastosować inną technikę, jeśli chcemy by nasze własne typy były składniowo maksymalnie podobne do wbudowanych.

Trzeba mianowicie przygotować je tak, by dało się do nich stosować operator [] niejako więcej niż raz. Wymaga to wprowadzenia jakiejś klasy pośredniej; dla macierzy może ona reprezentować pojedynczy wiersz:

  1. template <typename T> struct Matrix;
  2. template <typename T> struct MatrixRow
  3. {
  4.     friend class Matrix<T>;
  5.     private:
  6.         Matrix<T>& m_Matrix;
  7.         int m_Row;
  8.  
  9.         MatrixRow(Matrix<T>& matrix, int row)
  10.             : m_Matrix(matrix), m_Row(row) { }
  11.  
  12.     public:
  13.         T& operator[] (int col)
  14.             { return m_Matrix(m_Row, col); }
  15. };

Dla tego wiersza piszemy naturalnie zwykły operator indeksowania, pozwalający nam dostać się do jego elementów. Trik leży w postaci operatora, którą umieszczamy w samej klasie macierzy:

  1. template <typename T> struct Matrix
  2. {
  3.     public:
  4.         MatrixRow<T> operator[] (int row)
  5.             { return MatrixRow<T>(*this, row); }
  6.  
  7.         // "normalny" operator indeksowania za pomocą
  8.         // nawiasów okrągłych
  9.         T& operator() (int row, int col) { /* ... */ }
  10.  
  11.     // (reszta niezbyt ważna :))
  12. };

Zwraca ona nasz wiersz, a właściwie jego opakowanie, które to zdefiniowaliśmy. W ten sposób osiągamy dla Matrix<T>zachowanie niemal dokładnie analogiczne do tablic typu T**: pierwsza para nawiasów daje nam T* (u nas MatrixRow<T>), zaś druga konkretną wartość typu T:

  1. Matrix<int> mtx(4,4);
  2. mtx[2][2] = 42;

W tym rozwiązaniu oczywiście parę szczegółów do uwzględnienia (np. warianty const naszego operatora). Widać jednak, że jeśli bardzo chcemy, to przy odrobinie pomysłowości da się wszędzie używać “właściwych” nawiasów :)

Tags: ,
Author: Xion, posted under Programming »


3 comments for post “Kilka nawiasów kwadratowych”.
  1. lukaszw:
    September 13th, 2008 o 21:52

    Fajny sposób ;) Co prawda niekoniecznie przydatny, ale…

  2. Reg:
    September 14th, 2008 o 11:55

    Pytanie co z wydajnością? Ja generalnie nie wierzę w optymalizacje kompilatora i wątpię, żeby wyszedł z tego kod równie szybki, co z przeładowanego w macierzy operatora () (int row, int col).

  3. moriturius:
    September 19th, 2008 o 7:31

    Nie wiem czy najpierw powinno się zwracać wiersz czy kolumnę ale to inny problem ^^

    @lukaszw: Nie wiem czy nieprzydatny. Może przy grach komputerowych mniej przydatny, ale jak najbardziej stosowany. Kiedyś już coś podobnego gdzieś widziałem w użyciu i działało jak powinno.

    @Reg: w takim wypadku trzeba sie zapytac raczej czy wygoda uzycia zrekompensuje male straty w szybkosci ^^

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.