Производный от std :: back_insert_iterator?

Я хочу извлечь из std :: back_insert_iterator , чтобы создать своего рода фильтр для строкового типа, скажем, back_xml_insert_iterator , который будет проверять символы, передаваемые через него, и искать символы, которые не могут быть переданы «голыми» в поток XML, например, '' ', ' & ', ' <', '> ' и ' \ '' , и вместо этого будут вставлять ссылки на сущности своих персонажей, например, "& # 34; « для » ».

template<class StringType>
class back_xml_insert_iterator : public std::back_insert_iterator<StringType> {
  typedef std::back_insert_iterator<StringType> base_type;
public:
  typedef typename base_type::container_type container_type;
  typedef typename StringType::value_type value_type;

  explicit back_xml_insert_iterator( StringType &s ) : base_type( s ) { }

  back_xml_insert_iterator& operator=( value_type c ) {
    switch ( c ) {
      case '"':
      case '&':
      case '\'':
      case '<':
      case '>':
        char buf[10];
        this->container->append( "&#" );
        this->container->append( itoa( c, buf ) );
        this->container->push_back( ';' );
        break;
      default:
        this->container->push_back( c );
    }
    return *this;
  }
};

Это компилируется нормально. Когда я создал экземпляр, я подтвердил, что конструктор вызывается, но мой operator = () никогда не вызывается. Я думаю, это потому, что унаследованный оператор * () возвращает back_insert_iterator & , а не back_xml_insert_iterator & , поэтому back_insert_iterator :: operator = () вызывается, а не мой (поскольку operator = () не является и не может быть виртуальным ).

Если это так, то кажется невозможным извлечь из back_insert_iterator полезным способом.

Если вместо этого я создам свой собственный класс back_insert_iterator_base , например:

template<class ContainerType,class DerivedType>
class back_insert_iterator_base :
  public std::iterator<std::output_iterator_tag,void,void,void,void> {
public:
  typedef ContainerType container_type;

  DerivedType& operator*() {
    return *static_cast<DerivedType*>( this );
  } 

  DerivedType& operator++() {
    return *static_cast<DerivedType*>( this );
  }

  DerivedType& operator++(int) {
    return *static_cast<DerivedType*>( this );
  }

protected:
  back_insert_iterator_base( ContainerType &c ) : container( &c ) {
  }

  ContainerType *container;
};

, и получим из него, например:

template<class StringType>
class back_xml_insert_iterator :
  public back_insert_iterator_base< StringType, back_xml_insert_iterator<StringType> > {
  // ... as before ...
};

затем back_xml_insert_iterator работает как нужно. Так можно ли извлечь из std :: back_insert_iterator и заставить его работать как нужно?

Update

Вот как я хотел бы использовать back_xml_insert_iterator . Во-первых, была бы вспомогательная функция:

template<class StringType> inline
back_xml_insert_iterator<StringType> back_xml_inserter( StringType &s ) {
  return back_xml_insert_iterator<StringType>( s );
}

Тогда написание функции to_xml () было бы тривиально:

template<class InputStringType,class OutputStringType> inline
void to_xml( InputStringType const &in, OutputStringType *out ) {
  std::copy( in.begin(), in.end(), back_xml_inserter( *out ) );
}
1
задан Paul J. Lucas 16 August 2010 в 16:57
поделиться

3 ответа

Я мог бы заставить это работать с вызовом operator = успешно. Я правильно понял?

// пример back_insert_iterator

#include <iostream>
#include <iterator>
#include <vector>
using namespace std;

template<class StringType> 
class back_xml_insert_iterator : public std::back_insert_iterator<StringType> { 
   typedef std::back_insert_iterator<StringType> base_type; 
public: 
   typedef typename base_type::container_type container_type; 
   typedef typename StringType::value_type value_type; 

   explicit back_xml_insert_iterator( StringType &s ) : base_type( s ) { } 

   back_xml_insert_iterator& operator=( value_type c ) { 
      switch ( c ) { 
      case '"': 
      case '&': 
      case '\'': 
      case '<': 
      case '>': 
         break; 
      default: 
         this->container->push_back( c ); 
      } 
      return *this; 
   } 
}; 

int main () {
   vector<int> firstvector, secondvector;
   for (int i=1; i<=5; i++)
   { firstvector.push_back(i); secondvector.push_back(i*10); }

   back_xml_insert_iterator< vector<int> > back_it (firstvector);

   back_it = 2;   // It does call operator=

   copy (secondvector.begin(),secondvector.end(),back_it);

   vector<int>::iterator it;
   for ( it = firstvector.begin(); it!= firstvector.end(); ++it )
      cout << *it << " ";
   cout << endl;

   return 0;
}
0
ответ дан 2 September 2019 в 22:08
поделиться

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

Обычно лучше добавлять функциональность извне, либо предоставляя функторы, либо реализуя собственный итератор, что не должно быть действительно сложным. Вы можете взглянуть на библиотеку адаптера итератора в boost, если действительно хотите реализовать свои собственные итераторы.

2
ответ дан 2 September 2019 в 22:08
поделиться

Вы можете получить производный тип от back_insert_iterator, но вам придется переопределить операторы для возврата производного типа.

Делает ли это стратегию бессмысленной? Это зависит от вашего стиля кодирования. Переопределения будут просто вызывать базовый метод и возвращать this, так что в итоге вы получите код copy-paste, подобный тому, что вы вставили в back_insert_iterator_base.

template<class StringType>
class back_xml_insert_iterator : public std::back_insert_iterator<StringType> {
  typedef std::back_insert_iterator<StringType> base_type;
public:
  typedef typename base_type::container_type container_type;
  typedef typename StringType::value_type value_type;

    // add trivial overrides to avoid returning base type:
  back_xml_insert_iterator &operator++() { base::operator++(); return this; }
  back_xml_insert_iterator operator++(int) {
      back_xml_insert_iterator ret( *this );
      base::operator++();
      return ret;
  }

  explicit back_xml_insert_iterator( StringType &s ) : base_type( s ) { }

  ...

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

Обратите внимание, что частное наследование, с : private std::back_insert_iterator, добавит безопасности, предотвращая попытки пользователей использовать ваш класс как то, чем он не является.

0
ответ дан 2 September 2019 в 22:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: