7

私はクラス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(???) { ??? }

このようなコンストラクターは、2つの範囲[begin1、end1)と[begin2、end2)をmVecに連結します。課題は

1)mVecの定数を保持したいので、Xの他のメソッド全体で定数と見なされます。

2)不要なコピーはなるべく避けたい。つまり、1つの解決策は、範囲1に非定数一時を構築し、範囲2を挿入して返す静的メソッドを用意し、連結コンストラクターを次のように定義することです。

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

しかし、それはすべての値を少なくとも1回余分にコピーすると私は信じています。

4

5 に答える 5

9

いい問題です。2つの範囲を1つの範囲に変換する特定のイテレーターラッパータイプを実装しようとします。次の行の何か:

// 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) );

私はコードをチェックしていません。頭のてっぺんからここに入力しただけです。コンパイルできるかできないか、動作するかどうかはわかりませんが、これを開始点として使用できます。

于 2009-04-16T18:02:09.777 に答える
2

C ++の最高または最悪の機能の1つは、視点に応じて、仕事を遂行するために必要なときにそれを悪用できることです。この場合、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);
}

私は詳細のいくつかが間違っているかもしれません、私はこれをコンパイルしようとしませんでした。しかし、それはあなたにアイデアを与えるはずです。

于 2009-04-16T17:49:33.923 に答える
2

おそらくドロップするのが最善でしょうconst(とにかくなぜそれを主張するのですか?)。

それ以外の場合は、連結イテレータを作成する必要があります。これはかなり多くのコードです。詳細については、このスレッドを参照してください。

于 2009-04-16T17:29:35.517 に答える
1

1)mVecの定数を保持したいので、Xの他のメソッド全体で定数と見なされます。

  • constこれは、メンバー変数での奇妙な使用法です。そしてそれは良いデザインに逆らいます。定義上、構築はオブジェクトを変更する必要があるプロセスです。

  • オブジェクトを変更できないようにするための要件については、適切なカプセル化を使用してください。const-member関数を使用しmVecて、クラスのクライアントに基づいた機能を公開する必要があります。

2)不要なコピーはなるべく避けたい。つまり、1つの解決策は、範囲1に非定数一時を構築し、範囲2を挿入して返す静的メソッドを用意し、連結コンストラクターを次のように定義することです。

一般に、move-constructorsとr-value参照(C ++ 0xの約束された目標)を確認する必要があります。この記事を読んでください。

于 2009-04-16T19:21:06.420 に答える
1

コンパイラが行う最適化によっては、静的メソッドは思ったほど悪くない場合があります。また、C ++ 0xでは、moveコンストラクターは現在行われているコピーをすべて削除します。

それまでの間、ラッパーイテレータを使用してください。入力イテレータを実装するだけでよいので、コードはスレッドavakarがリンクするほど悪くはないでしょう。

于 2009-04-16T18:03:19.413 に答える