Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

pas de value_type pour output_iterator ...

3 réponses
Avatar
Alexandre Fournier
Bonjour,
Je travaille actuellement sur des polygones, J'ai créé une structure
segment qui contient deux références vers des points.
J'ai écrit une fonction qui me fournit une suite de segments (avec le
segment qui fait la fermeture) quand je lui rentre une suite de points.

Tout cela marchait à peu près correctement, sauf quand j'utilisais une
série de points const parce que la structure segment n'assurait pas le
caractère constant des références. Ce problème m'a poussé à réécrire la
classe segment de manière template qui s'instantie à la fois pour des
points et des points const.

Malheureusement, ma fonction de génération de segments ne compile plus,
car je lui passe un output_iterator (en réalité un back_inserter) en
argument, qui a le mauvais goût d'avoir un ::value_type de type void,
d'après le standard.

En conséquence, inverser les commentaires des lignes 46 et 47 et
respectivement 49 et 50 du code suivant génère des erreurs de compilation.

Quelqu'un aurait-il une façon de rémédier à celà ou alors dois-je
complètement revoir le "design" de cette classe segment ?

merci d'avance pour vos éclaircissements
A. Fournier

#include <iostream>
#include <vector>
#include <iterator>

template <typename T>
struct base_segment
{
T& first_;
T& second_;
base_segment(T& first,T& second)
: first_(first),
second_(second)
{}

base_segment& operator=(const base_segment<T>& rhs)
{
first_ = rhs.first_;
second_ = rhs.second_;
return *this;
}

};


typedef base_segment<int> segment ;
typedef base_segment<const int> const_segment;

template <typename input_iterator,
typename output_iterator>
output_iterator make_segments(
input_iterator begin_points,
input_iterator end_points,
output_iterator begin_segments)
{
typedef typename std::iterator_traits<output_iterator>::value_type
output_type;
typedef typename std::iterator_traits<input_iterator>::value_type
input_type;

input_iterator it1=begin_points;
input_iterator it2=begin_points;
++it2;
output_iterator it_output=begin_segments;


for(;it2!=end_points;++it1,++it2)
{
*it_output++=segment(*it1,*it2);
//*it_output++ = output_type(*it1,*it2);
}
*it_output++=segment(*it1,*begin_points);
//*it_output++ = output_type(*it1,*begin_points);
return it_output;
}



int main()
{
std::vector<int> points;
for(int i=0;i!=10;++i)
{
points.push_back(i);
}
std::vector<segment> my_segs;
//std::vector<const_segment> mysegs;

make_segments(
points.begin(),points.end(),
back_inserter(my_segs));
for(int i=0;i!=my_segs.size();++i)
{
std::cout<<i<<" : "<<my_segs[i].first_<<"
"<<my_segs[i].second_<<std::endl;
}

return EXIT_SUCCESS;
}

--
Alexandre Fournier
INRIA - Projet ARIANA
2004 route des Lucioles - BP 93
06902 Sophia-Antipolis Cedex
FRANCE
Tel : 04-92-38-77-73
courriel : alexandre.fournier@sophia.inria.fr

3 réponses

Avatar
Michel Decima
Tout cela marchait à peu près correctement, sauf quand j'utilisais une
série de points const parce que la structure segment n'assurait pas le
caractère constant des références. Ce problème m'a poussé à réécrire la
classe segment de manière template qui s'instantie à la fois pour des
points et des points const.

Malheureusement, ma fonction de génération de segments ne compile plus,
car je lui passe un output_iterator (en réalité un back_inserter) en
argument, qui a le mauvais goût d'avoir un ::value_type de type void,
d'après le standard.


Ah, tiens, c'est vrai. Je me rappelle deja etre tombe dessus, et je
m'etais demandé pourquoi ce choix...

Quelqu'un aurait-il une façon de rémédier à celà ou alors dois-je
complètement revoir le "design" de cette classe segment ?

merci d'avance pour vos éclaircissements
A. Fournier

#include <iostream>
#include <vector>
#include <iterator>

template <typename T>
struct base_segment
{
T& first_;
T& second_;
base_segment(T& first,T& second)
: first_(first),
second_(second)
{}

base_segment& operator=(const base_segment<T>& rhs)
{
first_ = rhs.first_;
second_ = rhs.second_;
return *this;
}

};


Pour la classe base_segment, je n'utiliserais pas des references pour
les membres, mais plutot des pointeurs. Ca va eviter les desagrements
du genre:

int main()
{
int x, y, z, q;
const_segment cs1( x, y );
const_segment cs2( z, q );

cs1 = cs2;
}

alex.cpp: In member function base_segment<T>&
base_segment<T>::operator=(const base_segment<T>&) [with T = const int]:
alex.cpp:31: instantiated from here
alex.cpp:16: erreur: assignment of read-only location
alex.cpp:17: erreur: assignment of read-only location


Pour la fonction make_segment, il y a une solution qui consiste a passer
par une classe intermediaire, avec les casts implicites qui vont bien:

template < typename T >
class temp_segment
{
public:
T* first_;
T* second_;
temp_segment(T& first,T& second)
: first_(&first),
second_(&second)
{}
operator base_segment< T >() const {
return base_segment< T >( *first_, *second_ );
}

operator base_segment< const T >() const {
return base_segment< const T >( *first_, *second_ );
}
};

Et alors make_segment devient:

template <typename input_iterator,
typename output_iterator

output_iterator make_segments(

input_iterator begin_points,
input_iterator end_points,
output_iterator begin_segments
)
{
typedef typename std::iterator_traits<input_iterator>::value_type
input_type;

input_iterator it1¾gin_points;
input_iterator it2¾gin_points;
++it2;
output_iterator it_output¾gin_segments;


for(;it2!=end_points;++it1,++it2)
{
*it_output++ = temp_segment< input_type >(*it1,*it2);
}
*it_output++ = temp_segment< input_type > (*it1,*begin_points);
return it_output;
}

Avec ca, ton programme de depart compile, pour vector<segment>
et vector<const_segment>.

Avatar
Michael DOUBEZ
Tout cela marchait à peu près correctement, sauf quand j'utilisais une
série de points const parce que la structure segment n'assurait pas le
caractère constant des références. Ce problème m'a poussé à réécrire
la classe segment de manière template qui s'instantie à la fois pour
des points et des points const.

Malheureusement, ma fonction de génération de segments ne compile
plus, car je lui passe un output_iterator (en réalité un
back_inserter) en argument, qui a le mauvais goût d'avoir un
::value_type de type void, d'après le standard.


Ah, tiens, c'est vrai. Je me rappelle deja etre tombe dessus, et je
m'etais demandé pourquoi ce choix...


Je suppose que l'opérateur *() renvoit un objet proxy qui permet de
faire l'insertion. Dans ce cas, un value_type signifiant aurait pu être
source d'erreur.

Maintenant, c'est vrai qu'il ne doit pas être courant de manipuler une
reference sur la valeur d'un output_iterator.

Michael


Avatar
Michel Decima

Malheureusement, ma fonction de génération de segments ne compile
plus, car je lui passe un output_iterator (en réalité un
back_inserter) en argument, qui a le mauvais goût d'avoir un
::value_type de type void, d'après le standard.


Ah, tiens, c'est vrai. Je me rappelle deja etre tombe dessus, et je
m'etais demandé pourquoi ce choix...


Je suppose que l'opérateur *() renvoit un objet proxy qui permet de
faire l'insertion. Dans ce cas, un value_type signifiant aurait pu être
source d'erreur.


Arf, bien sur, j'aurais du m'en douter. Merci pour la precision.