Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 16/10/2011, à 05:02

heudeubert

(c++)problème avec boost::iterator_adaptor

Bonjour !

J'essaie d'utiliser boost::iterator_adaptor, mais j'ai quelques soucis.
Mon premier problème :

#include    <boost/iterator_adaptors.hpp>
#include    <algorithm>
#include    <iostream>

template <class T>
class Reverse : public boost::iterator_adaptor <Reverse<T>, T> 
{
    public:
        Reverse( T IterTemp ) : Reverse<T>::iterator_adaptor_( IterTemp ) {};
        void increment( void )
        { --this->base_reference(); }
};

Je n'ai défini qu'increment(void), et non increment(int), decrement, etc. pour simplifier l'example, mais les définir donne le même résultat.
Quand je fais :

int main(void)
{
    int oh[] = { 3, 5, 7 };
    Reverse <int*> ItoBegin( oh + 2 ), ItoEnd( oh - 1 );
    std::copy( ItoBegin, ItoEnd, std::ostream_iterator<int>( std::cout, "\n" ) );
    return 0;    
}

Et que j'exécute :

$ g++ -Wall -o test test.cpp
$ ./test

Je n'ai pas d'erreur de compilation ni runtime, mais je n'obtient pas d'output. Tandis qu'avec :

void fonc( int a )
{ std::cout    << a << "\n"; }
int main(void)
{
    int oh[] = { 3, 5, 7 };
    Reverse <int*> ItoBegin( oh + 2 ), ItoEnd( oh - 1 );
    std::for_each( ItoBegin, ItoEnd, fonc );
    return 0;    
}

J'obtiens :

7
5
3

Comment cela se fait-il ?

Mon deuxième problème :
J'instancie mon Reverse via :

        Reverse( T IterTemp ) : Reverse<T>::iterator_adaptor_( IterTemp ) {};

parce que j'ai vu que cela semblait marcher. Cependant, je me demande pourquoi cette ligne est nécessaire. En effet, dans la définition d'iterator_adaptor dans <boost/iterator/iterator_adaptor.hpp>, on trouve :

  template <
      class Derived
    , class Base
    ...  >
  class iterator_adaptor
   ...  {
      ...
   public:
      ...
      explicit iterator_adaptor(Base const &iter)
          : m_iterator(iter)
      {
      }
      ...
   private:
      Base m_iterator;
  };

Or, quand j'essaie de retirer le constructor Reverse( T IterTemp ), pensant que le constructor ci-dessus suffira, j'obtiens :

...: In function ‘int main()’:
...:21:34: error: no matching function for call to ‘Reverse<int*>::Reverse(int*)’
...:7:1: note: candidates are: Reverse<int*>::Reverse()
...:7:1: note:                 Reverse<int*>::Reverse(const Reverse<int*>&)

Comme si le compilateur ignorait l'existence du constructor iterator_adaptor(Base const&iter), ou considérait que Base a pour type Reverse<int*>& et non int*.
Comment cela se fait-il ?

Merci beaucoup ! smile

Dernière modification par heudeubert (Le 16/10/2011, à 05:04)

Hors ligne

#2 Le 20/10/2011, à 12:19

heudeubert

Re : (c++)problème avec boost::iterator_adaptor

Problème 1 résolu :
Reverse<T> est considéré comme un itérateur random access, puisque T (int*) est random access. En conséquence, copy() calcule la distance entre  ItoBegin et ItoEnd avec "ItoEnd - ItoBegin" (il s'agit d'une optimisation : avec un iterator non-random access, copy effectue ce calcul via une succession de ++). Il faut donc définir distance_to(), en plus d'incrément() :

        typename boost::iterator_traits<Reverse<T> >::difference_type distance_to( const Reverse<T>& Other ) const
        { return this->base_reference() - Other.base_reference(); }

En gros, ma classe Reverse<T> faisaient de fausses assomptions sur le template (-> que T soit au maximum bidirectionnel, et pas random access).

Problème 2 (encore non résolu) :
J'ai réussi à le réduire à :

#include  <boost/iterator_adaptors.hpp>

template <class T>
struct Reverse : public boost::iterator_adaptor <Reverse<T>, T> 
{
    Reverse( T IterTemp ) : Reverse<T>::iterator_adaptor_( IterTemp ) {};
};

int main(void)
{
    int oh[] = { 10, 20, 30 };
    Reverse<int*> Iter( oh );
  return 0;    
}

Le problème est que je ne comprends pas pourquoi je dois rajouter "Reverse( T IterTemp ) : ... {};", dans la mesure où l'header définit déjà un constructor prenant un argument T (cf plus haut). J'ai l'impression que cela a à voir avec le fait que le premier élément du template de Reverse est son propre type ("Curiously Recurring Template Pattern (CRTP)"), ce qui perturbe le compilateur, mais j'aimerais réussir à mieux comprendre cette idée.
Merci smile

Hors ligne

#3 Le 20/10/2011, à 21:35

Le Farfadet Spatial

Re : (c++)problème avec boost::iterator_adaptor

Salut à tous !

heudeubert a écrit :

assomptions

   L’assomption est le privilège en vertu duquel la Vierge Marie a été transportée au ciel dès la fin de sa vie terrestre. Je doute que cela ait un quelconque rapport ici. Sinon, l’anglais « assumption » se traduit en français par « hypothèse » ou « supposition ».

   Oui, j’étais déjà un partisan de maître Capello avant, mais depuis mon retour des États-Unis, je suis devenu un intégriste anti anglicisme !

Le problème est que je ne comprends pas pourquoi je dois rajouter "Reverse( T IterTemp ) : ... {};", dans la mesure où l'header définit déjà un constructor prenant un argument T (cf plus haut). J'ai l'impression que cela a à voir avec le fait que le premier élément du template de Reverse est son propre type ("Curiously Recurring Template Pattern (CRTP)"), ce qui perturbe le compilateur, mais j'aimerais réussir à mieux comprendre cette idée.

   Bon, je reconnais avoir regardé un peu vite (et avoir un peu bêtement bloqué sur « assomption »), mais le message d’erreur me faisait plutôt penser à un problème entre type constant et type variable. Sinon, je ne comprends pas bien pourquoi cela te semble étrange d’avoir des prototypes homogènes. Je dois rater quelque chose…

   À bientôt.

Le Farfadet Spatial

Hors ligne