Sémantiques

Introduction

La question de la sémantique d’une classe, entité ou valeur, revient souvent en C++. Et bien que cette question devrait se poser lors de la conception, les discussions sur les forums nous montrent qu’elle a souvent besoin d’être reposée suite à un problème technique. Les problèmes qu’on retrouve le plus souvent et qui traduisent un mauvais choix de sémantique, voire plus généralement de conception, sont le slicing et un besoin de downcasting.

Ces problèmes apparaissent essentiellement lors de l’utilisation de l’héritage public. Dans mon article précédent j’ai présenté une alternative à l’introduction d’héritage public dans les interfaces, celui-ci se retrouve masqué. Je vais donc partir du postulat de l’absence d’héritage public pendant la phase de conception. Les questions liées à la sémantiques vont donc concerner les services spéciaux du langage.

Services spéciaux

Je les avais évoqués dans un articles précédent, et il me semble primordial d’y revenir pour parler de sémantique. Plus exactement je vais considérer :

  • Le constructeur de copie et celui par déplacement
template<class>
struct Box {
  Box(const Box&);
  Box(Box&&)

  //Versions généralisées
  template<class T>
  Box(const Box<T>&);
  template<class T>
  Box(Box<T>&&)
};
  • Les opérateurs d’affectation, normaux et par déplacements
template<class>
struct Box {
  Box& operator=(const Box&);
  Box& operator=(Box&&);

  //Versions généralisées
  template<class T>
  Box& operator=(const Box<T>&);
  template<class T>
  Box& operator=(Box<T>&&);
};

On peut déjà faire une remarque sur la sémantique d’ensemble d’instructions utilisant les versions par déplacement et l’utilitaire std::move.

Box<int> b1;
Box<int> b2(std::move(b1));
//Ici b1 n'est plus utilisable

Box<int> b1, b2;
Box<int> b2 = std::move(b1);
//Ici b1 n'est plus utilisable

La conséquence immédiate est qu’il n’y a pas de condition à priori sur l’existence de ces versions par déplacement. On va même supposer qu’elles existeront toujours, elles sont utiles pour stocker des objets dans des conteneurs et ne sont pas problématiques puisque l’on a exclu l’héritage public de la discussion.

J’ai toujours trouvé la distinction entre sémantique d’entité et sémantique de valeur subjective et non définie rigoureusement. Dans la suite je vais essayer de proposer un critère ayant une certaine rigueur, cependant je ne suis pas parvenu à exprimer un critère dépourvu de subjectivité.

Critère

S’il ne fallait retenir qu’un seul principe commun à l’ensemble de tous les langages, je pense que ce serait celui de moindre surprise. On peut exprimer ce principe pour l’interaction entre un opérateur de comparaison et les services spéciaux :

Box<int> b1;
Box<int> b2(b1);
b1 == b2;
//Toujours vrai

Box<int> b1, b2;
b2 = b1;
b1 == b2;
//Toujours vrai

Notons que l’opérateur de comparaison n’a pas besoin de réellement exister, il peut être totalement fictif.

Voyons la part de subjectivité : on suppose une relation d’ordre naturelle permettant d’ordonner les opérateurs de comparaison. La subjectivité est bien évidemment cette notion de relation d’ordre naturelle.

Notons l’existence d’un opérateur de comparaison particulier qui pourrait toujours exister et que je qualifierais pas la suite de trivial :

template<class T, class U>
bool operator==(const Box<T>& lhs, const Box<U>& rhs)
{ return &lhs == &rhs; }

Il n’y a aucun intérêt de définir réellement cet opérateur dans le code, la signification serait que deux objets distincts sont différents. Or le seul intérêt de la comparaison est d’ordonner des objets distincts, ce qui est de facto inutile s’ils sont tous différents (on a l’ordre que l’on veut à une permutation près). Il doit rester fictif et ne va nous servir que pour la définition du critère.

Je vais postuler le critère suivante :

  • On a une sémantique d’entité si et seulement si l’opérateur de comparaison trivial est strictement plus naturel que tout autre opérateur de comparaison
  • On a une sémantique de valeur si et seulement si on a pas de sémantique d’entité

Implication

J’ai déjà mentionné le fait qu’il n’y a pas d’implication sur les services spéciaux par déplacement. Concernant les services spéciaux par copie, il y en a :

  • Dans le cas de la sémantique de valeur, on a une simple condition sur ces services
  • Dans le cas de la sémantique d’entité, on a une contradiction, ces services ne doivent donc pas exister

Notons que quand les services spéciaux par copie et par déplacement existent, ceux par déplacement sont sémantiquement redondants avec ceux par copie, i.e. ils n’apportent rien de plus. Le seul intérêt dans ce cas est celui d’une optimisation.

Pour l’opérateur de comparaison, si celui-ci existe ce ne peut être que celui qui est strictement plus naturel que tout autre opérateur. Il y a deux cas :

  • Dans celui de la sémantique d’entité, il ne doit pas exister puisque le strictement plus naturel est le trivial est qu’il n’a pas de raison d’exister réellement
  • Dans celui de la sémantique de valeur, s’il y a un opérateur strictement plus naturel que tout autre opérateur, alors cet opérateur doit être défini. Sinon aucun opérateur ne peut être défini.

Conclusion

En guise de conclusion, je vais faire le lien avec les implications que l’on considère usuellement avec les sémantiques. Tout d’abord ce qui est commun avec ce que je présente ici :

  • Les services spéciaux par copie existent uniquement pour la sémantique de valeur
  • L’opérateur de comparaison peut uniquement exister pour la sémantique de valeur
  • Les services spéciaux par déplacement peuvent exister pour la sémantique de valeur

Il y a cependant un point qui change, usuellement les services spéciaux par déplacement ne doivent pas exister pour la sémantique d’entité, alors que je présente ici une sémantique d’entité qui inclut nécessairement les services spéciaux par déplacement. Cette différence tient uniquement dans le fait que j’exclus l’héritage public de la phase de conception.

Dernier point, je fais référence en introduction à ce que je présente dans mon précédent article comme alternative à l’héritage public pour le polymorphisme. En réalité boost demande l’existence de services spéciaux par copie pour faire fonctionner cette alternative, il est nécessaire de patcher boost pour rendre les choses utilisables avec les services spéciaux par déplacement.

Merci à Winjerome pour la relecture orthographique.

Publicités

Une réflexion au sujet de « Sémantiques »

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s