38 votes

Rendre incompatibles des alias de type C ++ identiques

J'utilise std::vector<int> pour les deux types d'information. Je veux être sûr que je n'ai pas accidentellement mélanger les deux utilisations.

En bref, je veux quelque chose comme ce morceau de code à l'échec:

#include <vector>

using A = std::vector<int>;
using B = std::vector<int>;

void fa(const A&);
void fb(const B&);

void fun()
{
    A ax;
    B bx;

    fa(bx);
    fb(ax);
}

Ce code compile, même si fa attend un argument de type A. De toute évidence, A et B sont identiques.

Quelle est la manière la plus simple de ce code compile correctement:

fa(ax);
fb(bx);

et faire de ce code échouer:

fa(bx);
fb(ax);

Bien sûr, je peux enrouler std::vector<int> dans une autre classe, mais alors je vais avoir besoin de réécrire son interface. Sinon, je pourrais hériter d' std::vector<int>, mais cela est souvent découragé.

En bref, j'ai besoin de deux versions incompatibles de std::vector<int>.

MODIFIER

Il a été suggéré que la Forte typedefs peut résoudre ce problème. Ce n'est que partiellement vrai. Si j'utilise BOOST_STRONG_TYPEDEF(std::vector<int>, A), j'ai besoin d'ajouter un peu ennuyeux jette. Par exemple, au lieu de

A ax{1,3,5};

J'ai besoin d'utiliser

A ax{std::vector<int>{1,3,5}};

Et au lieu de

for (auto x : ax) ...

J'ai besoin d'utiliser

for (auto x : (std::vector<int>)ax) ...

60voto

Christoph Points 909

Je pense que ce que vous voulez, c'est encore le meilleur avec:

struct A : public std::vector<int>{
  using vector::vector;
};
struct B : public std::vector<int>{
  using vector::vector;
};

C'est exactement ce que vous voulez. Il n'y a pas de raison de venir avec une moche hackery simplement pour éviter un propre déclaration. La principale raison je vois que ces sous-typage est pas favorisée, c'est que la même chose devrait se comporter comme ils sont les mêmes et peuvent être utilisés de façon interchangeable. Mais c'est exactement ce que vous souhaitez supprimer, et donc de sous-typage, il fait exactement la déclaration que vous voulez: ils ont la même interface, mais ils ne devraient pas être utilisée de la même parce qu'ils ne sont pas les mêmes.

14voto

Bart van Nierop Points 1427

D'une façon ou d'une autre, c'est une affaire de primitives obsession. Soit l' ints représentent vraiment quelque chose et l' vectors sont une collection de quelque chose, ou l' vector<int>s représentent quelque chose.

Dans les deux cas, cela devrait être résolu par emballage de la primitive en quelque chose de plus significatif. Par exemple:

class column
{
  int id;
  /*...*/
}; 
class row
{
  int id;
  /*...*/
};

std::vector<row> et std::vector<column> ne serait pas interchangeables.

Bien sûr, la même idée pourrait être appliquée à d' vector<int> au lieu de int, si vector<int> est la primitive qui signifie vraiment quelque chose d'autre.

4voto

Biagio Festa Points 4698

Alternativement, je pourrais hériter de std :: vector, mais cela est souvent déconseillé.

OMI, cela dépend de la situation. En général, pourrait être une bonne solution

 #include <vector>

class VectorA :
    public std::vector<int> {
 public:
  VectorA() = default;
  ~VectorA() = default;
  VectorA(const VectorA&) = default;
  VectorA(VectorA&&) = default;
  VectorA& operator=(const VectorA&) = default;
  VectorA& operator=(VectorA&&) = default;
};

class VectorB :
    public std::vector<int> {
 public:
  VectorB() = default;
  ~VectorB() = default;
  VectorB(const VectorB&) = default;
  VectorB(VectorB&&) = default;
  VectorB& operator=(const VectorB&) = default;
  VectorB& operator=(VectorB&&) = default;
};
 

Vous pouvez toujours utiliser VectorA et VectorB comme vecteur normal, mais vous ne pouvez pas basculer entre eux.

 void acceptA(const VectorA& v) {
  // do something
}

void acceptB(const VectorB& v) {
  // do something
}

template<typename T>
void acceptVector(const std::vector<T>& v) {
  // do something
}

int main(int argc, char *argv[]) {
  VectorA va;
  VectorB vb;

  acceptA(va);  // you can only pass VectorA
  acceptB(vb);  // same here for VectorB

  acceptVector(va);  // any vector
  acceptVector(vb);

  return 0;
}
 

4voto

Pete Kirkham Points 32484

C'est en partie pourquoi vous pouvez faire de la programmation orientée objet en C ++ ainsi que de la programmation par objet réutilisant les types de bibliothèque.

Créez des classes A et B qui modélisent le comportement de votre domaine. Peu importe s'il arrive que les deux comportements soient implémentés avec des champs vecteurs d'ints; tant que vous ne couperez pas l'encapsulation, toutes les opérations sur les vecteurs distincts seront dans l'étendue de leur classe et aucune confusion ne peut se produire.

 #include <vector>

class A {
    std::vector<int> cake_orders_;

public:
    void f() ; // can only do something to do with cake
};

class B {
    std::vector<int> meal_worm_lengths_;

public:
    void f() ; // can only do something to do with worms
};


void fun()
{
    A ax;
    B bx;

    a.f(); // has to be the right thing
    b.f();
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X