Ne déteste-tu pas quand tu as
class Foobar {
public:
Something& getSomething(int index) {
// big, non-trivial chunk of code...
return something;
}
const Something& getSomething(int index) const {
// big, non-trivial chunk of code...
return something;
}
}
Nous ne pouvons implémenter aucune de ces méthodes avec l'autre, car vous ne pouvez pas appeler la version nonconst
à partir de la version const
(erreur du compilateur). Un cast sera nécessaire pour appeler la version const
à partir de la version non -const
.
Existe-t-il une vraie solution élégante à cela, sinon, quelle est la plus proche de celle-ci?
Je me souviens d'un des livres de C++ efficace que la façon de le faire est d'implémenter la version non-const en supprimant le const de l'autre fonction.
Ce n'est pas particulièrement joli, mais c'est sûr. Étant donné que la fonction membre qui l'appelle est non-const, l'objet lui-même est non-const et le rejet de la const est autorisé.
class Foo
{
public:
const int& get() const
{
//non-trivial work
return foo;
}
int& get()
{
return const_cast<int&>(const_cast<const Foo*>(this)->get());
}
};
Que diriez-vous:
template<typename IN, typename OUT>
OUT BigChunk(IN self, int index) {
// big, non-trivial chunk of code...
return something;
}
struct FooBar {
Something &getSomething(int index) {
return BigChunk<FooBar*, Something&>(this,index);
}
const Something &getSomething(int index) const {
return BigChunk<const FooBar*, const Something&>(this,index);
}
};
De toute évidence, vous aurez toujours la duplication de code objet, mais pas de duplication de code source. Contrairement à l'approche const_cast, le compilateur vérifiera votre const-correctness pour les deux versions de la méthode.
Vous devez probablement déclarer les deux instanciations intéressantes de BigChunk comme amis de la classe. C'est une bonne utilisation de friend, car les fonctions friend sont cachées près du friendee, il n'y a donc aucun risque de couplage sans contrainte (ooh-er!). Mais je n'essaierai pas la syntaxe pour le faire maintenant. N'hésitez pas à ajouter.
Il y a des chances que BigChunk ait besoin de se déférer, auquel cas l'ordre de définition ci-dessus ne fonctionnera pas très bien, et certaines déclarations avancées seront nécessaires pour le trier.
De plus, afin d'éviter une recherche vide de BigChunk dans l'en-tête et de décider d'instancier et de l'appeler même si c'est moralement privé, vous pouvez déplacer le tout dans le fichier cpp pour FooBar. Dans un espace de noms anonyme. Avec liaison interne. Et un panneau disant "méfiez-vous du léopard".
Je jetterais le const au non-const (deuxième option).
Pourquoi ne pas simplement extraire le code commun dans une fonction privée distincte, puis demander aux deux autres de l'appeler?
Essayez d'éliminer les getters en refactorisant votre code. Utilisez des fonctions ou des classes d'amis si seulement un très petit nombre d'autres choses ont besoin de quelque chose.
En général, les Getters et Setters rompent l'encapsulation car les données sont exposées au monde. L'utilisation d'un ami n'expose les données qu'à quelques privilégiés, ce qui permet une meilleure encapsulation.
Bien sûr, ce n'est pas toujours possible, vous pouvez donc être coincé avec les getters. À tout le moins, la plupart ou la totalité du "morceau de code non trivial" devrait être dans une ou plusieurs fonctions privées, appelées par les deux getters.
La référence const
à l'objet est logique (vous mettez une restriction sur l'accès en lecture seule à cet objet), mais si vous devez autoriser une référence nonconst
, vous pourriez aussi bien bien rendre le membre public.
Je crois que c'est à la Scott Meyers (Efficient C++).
Le concept de "const" est là pour une raison. Pour moi, il établit un contrat très important sur la base duquel des instructions supplémentaires d'un programme sont écrites. Mais vous pouvez faire quelque chose sur les lignes suivantes: -
Avec cela, on peut utiliser une référence const sur le LHS si vous avez besoin de maintenir la fonctionnalité const où vous utilisez le getter avec l'utilisation non const (dangereux). Mais il incombe maintenant au programmeur de maintenir les invariants de classe.
Comme cela a été dit dans SO avant, rejetant la constance d'un objet const initialement défini et l'utilisant est un UB donc je n'utiliserais pas de transtypages. Faire également un objet non const const puis à nouveau rejeter la constance ne serait pas trop beau.
Une autre directive de codage que j'ai vue utilisée dans certaines équipes est: -
Cela permet une certaine cohérence dans la base de code globale et l'appelant peut clairement voir quels appels peuvent modifier la variable membre.