C++: itérateur générique renvoyé par la fonction membre d'une classe abstraite

a marqué ce sujet comme résolu.

Bonsoir, J’ai un problème de design en C++ et je ne sais pas par quel bout le prendre.
Voici en gros l’idée :

class OptimizationModel {
public:
  virtual TypeIterateurSurEntiers get_equality_constraints() const = 0;
};

class ScaledModel: public OptimizationModel {
public:
  TypeIterateurSurEntiers get_equality_constraints() const override;
};

class EqualityConstrainedModel: public OptimizationModel {
public:
  TypeIterateurSurEntiers get_equality_constraints() const override;
};

J’ai une classe abstraite qui définit une fonction membre qui sera implémentée par plusieurs sous-classes. Cette fonction retourne un itérateur sur entiers, de telle sorte que je peux l’appeler dans une "range-based for loop" :

for (size_t constraint_index: problem.get_equality_constraints()) {
  ...
}

Suivant la sous-classe (ScaledModel ou EqualityConstrainedModel), j’aimerais pouvoir retourner un itérateur sur un container classique (par exemple std::vector<size_t>) ou bien mon propre type itérable (dans ce genre) pour éviter les allocations dynamiques.
Existe-t-il une technique en C++<=17 qui permette de faire cela ?
Merci d’avance,

Charlie

En fait, les itérateurs sont spécifiques à chacun des conteneurs, ils ne suivent pas une hierarchie de type. Les fonctions qui manipulent tout type d’itérateur peuvent le faire parce que ces itérateurs répondent à certaines attentes, on sait comment les manipuler, donc on peut le faire au travers un template.

M’est d’avis que si tu veux interfacer un conteneur qui a de multiples implémentations, tu dois aussi interfacer son itérateur qui aura de multiples implémentations. Alors je pense que tu devrais faire une classe d’itérateur qui correspond à ton retour de fonction, puis la dériver en deux classes filles qui seront des wrappers aux itérateurs de vector et de ton itérateur perso.

Cette solution me parait la plus adaptée à la question que tu poses, mais j’ai l’impression que de meilleures solutions pourraient exister en remaniant l’architecture pour éviter l’héritage en faisant par exemple de la composition. Mais il faudrait plus d’info, et ça apparait pas aussi évidemment pour moi.

EDIT: Ou bien selon ce que tu vas faire, tu peux peut-être trouver une solution à base de template, comme par exemple template OptimizationModel pour préciser quel conteneur utiliser. Par contre ScaledModel et EqualityConstraintModel n’hériteront plus de la même chose, est-ce vraiment important ?

EDIT 2 : En relisant, je m’apperçois que tu fais une confusion sur l’itérateur. Pour pouvoir appeler get_equality_constraints() dans une for-range loop

for (size_t constraint_index: problem.get_equality_constraints()) {
  ...
}

Il faut que get_equality_constraints renvoie un conteneur, pas un itérateur. "un conteneur", c’est à dire ? Et bien sur la page du range-for on peut voir qu’il y a plusieurs possibilités : la plus simple est d’avoir une instance de classe qui implémente begin() et end() (qui, eux, renvoient éventuellement l’itérateur, ou dumoins quelque chose qui implémente ++), mais sinon on peut voir qu’il est possible d’avoir juste des implémentations des fonctions libres begin(const CustomType &) et end(const CustomType &). Bref, ton problème en fait n’est pas sur l’itérateur, mais sur le conteneur que ça devrait renvoyer.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte