Конкатенация итератора C ++ входит в константную векторную переменную-член во время построения - PullRequest
7 голосов
/ 16 апреля 2009

У меня есть класс X, который я предоставляю здесь:

class X {
  public:
    template <typename Iter>
    X(Iter begin, Iter end) : mVec(begin, end) {}

  private:
    vector<Y> const mVec;
};

Теперь я хочу добавить в этот класс новый конструктор конкатенации, что-то вроде:

template <typename Iter1, typename Iter2>
X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) : mVec(???) { ??? }

Такой конструктор объединит два диапазона [begin1, end1) и [begin2, end2) в mVec. Задачи

1) Я хотел бы сохранить константу в mVec, чтобы она считалась постоянной во всех других методах X.

2) Я бы хотел избежать ненужных копий, если это возможно. То есть одно решение состоит в том, чтобы иметь статический метод, который создает неконстантную временную переменную для диапазона 1, вставляет диапазон 2 и возвращает его, а затем определяет конструктор объединения для

template <typename Iter1, typename Iter2>
X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) 
  : mVec(concatenate(begin1, end1, begin2, end2)) { }

но это копирует все значения, по крайней мере, еще один раз, я полагаю.

Ответы [ 5 ]

9 голосов
/ 16 апреля 2009

Хорошая проблема. Я хотел бы попытаться реализовать конкретный тип оболочки итератора, который превращает два диапазона в один диапазон. Что-то в строках:

// compacted syntax for brevity...
template <typename T1, typename T2>
struct concat_iterator
{
public:
   typedef std::forward_iterator_tag iterator_category;
   typedef typename iterator_traits<T1>::value_type value_type;
   typedef *value_type pointer; 
   typedef &value_type reference;

   concat_iterator( T1 b1, T1 e1, T2 b2, T2 e2 ) 
      : seq1( b1 ), seq1end( e1 ), seq2( b2 ), seq2end( e2 );
   iterator& operator++() {
      if ( seq1 != seq1end ) ++seq1;
      else ++seq2;
      return this;
   }
   reference operator*() {
      if ( seq1 != seq1end ) return *seq1;
      else return *seq2;
   }
   pointer operator->() {
      if ( seq1 != seq1end ) return &(*seq1);
      else return &(*seq2);
   }
   bool operator==( concat_iterator const & rhs ) {
      return seq1==rhs.seq1 && seq1end==rhs.seq2 
          && seq2==rhs.seq2 && seq2end==rhs.seq2end;
   }
   bool operator!=( contact_iterator const & rhs ) {
      return !(*this == rhs);
   }
private:
   T1 seq1;
   T1 seq1end;
   T2 seq2;
   T2 seq2end;
};

template <typename T1, typename T2>
concat_iterator<T1,T2> concat_begin( T1 b1, T1 e1, T2 b2, T2 e2 )
{
   return concat_iterator<T1,T2>(b1,e1,b2,e2);
}
template <typename T1, typename T2>
concat_iterator<T1,T2> concat_end( T1 b1, T1 e1, T2 b2, T2 e2 )
{
   return concat_iterator<T1,T2>(e1,e1,e2,e2);
}

Теперь вы можете использовать:

 class X {
 public:
    template <typename Iter, typename Iter2>
    X(Iter b1, Iter e1, Iter2 b2, Iter2 e2 ) 
      : mVec( concat_begin(b1,e1,b2,e2), concat_end(b1,e1,b2,e2) ) 
    {}

  private:
    vector<Y> const mVec;
};

или (я только что подумал об этом) вам не нужно повторно декларировать ваш конструктор. Заставьте вашего абонента использовать вспомогательные функции:

X x( concat_begin(b1,e1,b2,e2), concat_end(b1,e1,b2,e2) );

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

2 голосов
/ 16 апреля 2009

Одна из лучших или худших особенностей C ++, в зависимости от вашей точки зрения, заключается в том, что вы можете злоупотреблять ею, когда это необходимо для выполнения работы. В этом случае const_cast является жертвой:

template <typename Iter1, typename Iter2>
X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) : mVec(begin1, end1) {
    const_cast<vector<Y>&>(mVec).insert(mVec.end(), begin2, end2);
}

Возможно, некоторые детали неверны, я не пытался это скомпилировать. Но это должно дать вам идею.

2 голосов
/ 16 апреля 2009

Вероятно, было бы лучше отбросить const (зачем вам на этом настаивать?).

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

1 голос
/ 16 апреля 2009

1) Я хотел бы сохранить константу в mVec, чтобы она считалась постоянной во всех других методах X.

  • Это любопытное использование const для переменной-члена. И это бросает вызов хорошему дизайну. По определению, строительство - это процесс, который требует изменения объекта.

  • Что касается вашего требования сохранить объект неизменяемым - используйте правильную инкапсуляцию. Вы должны использовать функции const -member для раскрытия любой функциональности на основе вашего mVec для клиентов вашего класса.

2) Я хотел бы избежать ненужных копий, если это возможно. То есть одно решение состоит в том, чтобы иметь статический метод, который создает неконстантную временную переменную для диапазона 1, вставляет диапазон 2 и возвращает его, а затем определяет конструктор объединения для

Вы должны смотреть на конструкторы перемещения и ссылки на r-значения в целом (обещанная цель C ++ 0x). Прочитайте эту статью .

1 голос
/ 16 апреля 2009

Ваш статический метод может быть не таким плохим, как вы думаете, в зависимости от оптимизации вашего компилятора. А в C ++ 0x конструкторы перемещения удаляют любое текущее копирование.

А пока идите с итератором-оболочкой. Код, скорее всего, будет не таким плохим, как поток, на который ссылается avakar, поскольку вам нужно только реализовать input итератор .

...