Элегантный способ для рекурсивного шаблона C ++ сделать что-то другое с классом листьев? - PullRequest
1 голос
/ 26 мая 2010

У меня есть шаблон класса C ++, который делает массив указателей. Это также получает typedefed для создания Arrays of Arrays и так далее:

typedef Array<Elem>           ElemArray;
typedef Array<ElemArray>      ElemArrayArray;
typedef Array<ElemArrayArray> ElemArrayArrayArray;

Я хотел бы иметь возможность установить один листовой узел из другого, скопировав указатель, чтобы они оба ссылались на один и тот же элемент.

Но я также хочу иметь возможность установить один массив (или массив массивов и т. Д.) Из другого. В этом случае я не хочу копировать указатели, я хочу держать массивы отдельно и опускаться в каждый из них, пока не доберусь до конечного узла, где я, наконец, скопирую указатели.

У меня есть код, который делает это (ниже). Когда вы устанавливаете что-либо в массиве, он вызывает метод CopyIn для копирования.

Но так как это шаблонно, ему также нужно вызвать метод CopyIn в листовом классе, что означает, что я должен добавить фиктивный метод в каждый листовой класс, который просто возвращает false.

Я также попытался добавить флаг в шаблон, чтобы сообщить ему, содержит ли он массивы или нет, и, таким образом, вызывать ли метод CopyIn. Это работает отлично - метод CopyIn конечных узлов никогда не вызывается, но он все еще должен быть там, чтобы компиляция работала!

Есть ли лучший способ сделать это?

#include <stdio.h>

class Elem {
public:
  Elem(int v) : mI(v) {}
  void Print() { printf("%d\n",mI); }
  bool CopyIn(Elem *v) { return false; }
  int mI;
};

template < typename T > class Array {
public:
  Array(int size) : mB(0), mN(size) {
    mB = new T* [size];
    for (int i=0; i<mN; i++) 
      mB[i] = new T(mN);
  }
  ~Array() {
    for (int i=0; i<mN; i++) 
      delete mB[i];
    delete [] mB;
  }
  T* Get(int i) { return mB[i]; }
  void Set(int i, T* v) { 
    if (! mB[i]->CopyIn(v) ) {
      // its not an array, so copy the pointer
      mB[i] = v; 
    }
  }
  bool CopyIn(Array<T>* v) {
    for (int i=0; i<mN; i++)  {
      if (v && i < v->mN ) {
        if ( ! mB[i]->CopyIn( v->mB[i] )) {
          // its not an array, so copy the pointer
          mB[i] = v->mB[i];
        }
      }
      else {
        mB[i] = 0;
      }
    }
    return true;  // we did the copy, no need to copy pointer
  }
  void Print() { 
    for (int i=0; i<mN; i++) {
      printf("[%d] ",i);
      mB[i]->Print();
    }
  }
 private:
  T **mB;
  int mN;
};

typedef Array<Elem>           ElemArray;
typedef Array<ElemArray>      ElemArrayArray;
typedef Array<ElemArrayArray> ElemArrayArrayArray;

int main () {
  ElemArrayArrayArray* a = new ElemArrayArrayArray(2);
  ElemArrayArrayArray* b = new ElemArrayArrayArray(3);

  // In this case I need to copy the pointer to the Elem into the ElemArrayArray
  a->Get(0)->Get(0)->Set(0, b->Get(0)->Get(0)->Get(0));

  // in this case I need go down through a and b until I get the to Elems
  //  so I can copy the pointers
  a->Set(1,b->Get(2));

  b->Get(0)->Get(0)->Get(0)->mI = 42;  // this will also set a[0,0,0]
  b->Get(2)->Get(1)->Get(1)->mI = 96;  // this will also set a[1,1,1]

  // should be 42,2, 2,2,  3,3, 3,96
  a->Print();

}

Ответы [ 2 ]

0 голосов
/ 27 мая 2010

Я немного перестроил код, чтобы использовать специализацию шаблонов, теперь обычный Set делает глубокую копию, как это:

   void Set(int i, T* v) {    mB[i]->CopyIn(v);   }

но для массива, содержащего листья, нужно добавить специализацию:

   template <> void Array<Elem>::Set(int i, Elem* v) {  SetPtr(i,v); }

Конечно, вы должны добавить новый для каждого листового класса. По крайней мере, если вы забудете это сделать, вы получите ошибку компилятора, поскольку он не может найти CopyIn в классе Elem.

Я думал о замене CopyIn перегруженным оператором присваивания, но это потеряло бы предупреждение компиляции, если вы забыли добавить специализацию.

Я также думал о создании оператора [] вместо Get и Set (только для этого примера), но кажется немного опасным, когда все эти указатели летают - легко написать [0] вместо (* a) [0]. Также я не мог отработать магию, необходимую для преобразования указателя в массиве в правильный тип ссылки для возврата [] (кто-нибудь знает, как это сделать?).

#include <stdio.h>

class Elem {
public:
  Elem(int v) : mI(v) {}
  void Print() { printf("%d\n",mI); }
  int mI;
};

template < typename T > class Array {
public:
  Array(int size) : mB(0), mN(size) {
    mB = new T* [size];
    for (int i=0; i<mN; i++) 
      mB[i] = new T(mN);
  }
  ~Array() {
    for (int i=0; i<mN; i++) 
      delete mB[i];
    delete [] mB;
  }
  T* Get(int i) { return (i<mN) ? mB[i] : 0; }
  void Set(int i, T* v)       { mB[i]->CopyIn(v); }
  void SetPtr(int i, Elem* v) { mB[i] = v; }
  bool CopyIn(Array<T>* v) {
    for (int i=0; i<mN; i++)  {
      if (v && i < v->mN ) {
    Set( i, v->Get(i) );
      }
      else {
        mB[i] = 0;
      }
    }
  }

  void Print() { 
    for (int i=0; i<mN; i++) {
      printf("[%d] ",i);
      mB[i]->Print();
    }
  }
 private:
  T** mB;
  int mN;
};


typedef Array<Elem>           ElemArray;
typedef Array<ElemArray>      ElemArrayArray;
typedef Array<ElemArrayArray> ElemArrayArrayArray;

template <> void Array<Elem>::Set(int i, Elem* v) {  SetPtr(i,v); }

int main () {
  ElemArrayArrayArray* a = new ElemArrayArrayArray(2);
  ElemArrayArrayArray* b = new ElemArrayArrayArray(3);

  // In this case I need to copy the pointer to the Elem into the ElemArrayArray
  a->Get(0)->Get(0)->Set(0, b->Get(0)->Get(0)->Get(0));

  // in this case I need go down through a and b until I get the to Elems
  //  so I can copy the pointers
  a->Set(1,b->Get(2));

  b->Get(0)->Get(0)->Get(0)->mI = 42;  // this will also set a[0,0,0]
  b->Get(2)->Get(1)->Get(1)->mI = 96;  // this will also set a[1,1,1]

  // should be 42,2, 2,2,  3,3, 3,96
  a->Print();

}
0 голосов
/ 26 мая 2010

Вы можете использовать специализацию шаблонов, чтобы сделать что-то другое с классом листьев:

template <> class Array<Elem> {
    // Define how Array<Elem> should behave
}

Как уже упоминалось в комментариях, вы должны рассмотреть возможность использования оператора [] вместо методов Get и Set для индексов массива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...