Question

Je travaille sur un code qui effectue les requêtes du plus proche voisin. Il existe deux idées simples expliquant comment un utilisateur peut interroger des données dans une recherche:

  • N points les plus proches d'un point de l'espace donné.
  • tous les points d'une distance donnée.

Dans mon code, les points sont placés dans une liste de points et ce dernier est un conteneur qui a pour tâche de garder une trace des points trouvés dans la recherche.

À l'heure actuelle, mon objet PointList a un constructeur:

PointList( unsigned int maxvals ); // #1

Les deux constructeurs suivants que je voudrais ajouter sont les suivants:

PointList( float maxdist ); // #2
PointList( unsigned int maxvals, float maxdist ); // #3

Ma question est la suivante: comment puis-je m'assurer que mes utilisateurs et le compilateur C ++ généreront le bon constructeur pour la liste de points et différencie les constructeurs 1 et 2? Devrais-je simplement mettre en œuvre le n ° 3 et fournir des constantes qui définissent les grandes valeurs pour maxvals et maxdist? Une autre alternative pourrait être d’écrire un autre système d’objets légers qui gouverne la logique d’ajout de points à la liste, mais cela semble excessif pour une idée aussi simple.

J'essaie vraiment de rendre cela transparent pour mes utilisateurs, qui sont pour la plupart des scientifiques qui ont appris le C ++ parfois sans le bénéfice d'une éducation formelle. Merci!

Était-ce utile?

La solution

La résolution de surcharge pour les types entiers se produit sur deux catégories, qui peuvent être résumées de manière très approximative en

.
  • Promotion: conversion des types inférieurs à int en unsigned int ou float, selon que double peut ou non stocker toutes les valeurs du type source.
  • Conversion: conversion de tout type entier en un autre type entier.

De même, la conversion des types à virgule flottante s'effectue sur deux catégories

  • Promotion: Ceci est une conversion de <=> à <=>
  • Conversion: conversion de tout type à virgule flottante en autre type à virgule flottante

Et il y a une conversion d'entier en flottant ou en arrière. Ceci est classé comme une conversion plutôt qu'une promotion. Une promotion est mieux classée qu'une conversion et, dans ce cas, seule une promotion est nécessaire. Ainsi, vous pouvez utiliser les constructeurs suivants

PointList( int maxVals );
PointList( unsigned int maxVals );
PointList( long maxVals );
PointList( unsigned long maxVals );

PointList( double maxDist );
PointList( long double maxDist );

Pour tout type entier, cela devrait sélectionner le premier groupe de constructeurs. Et pour tout type à virgule flottante, cela devrait sélectionner le deuxième groupe de constructeurs. Vos deux constructeurs d'origine peuvent facilement générer une ambiguïté entre <=> et <=> si vous passez un <=>, par exemple. Pour l’autre, le constructeur à deux arguments, vous pouvez choisir votre solution si vous le souhaitez.

Cela dit, j’utiliserais aussi une fonction usine, car je pense que la signification du paramètre est assez fragile. La plupart des gens s’attendent à ce que le résultat suivant soit égal à

PointList p(floor(1.5));
PointList u((int)1.5);

Mais cela entraînerait un état de choses différent.

Autres conseils

Pourquoi ne pas utiliser les méthodes de fabrique au lieu de constructeurs? Les méthodes d'usine ont l'avantage des noms personnalisables.


static PointList createNearestValues(unsigned int maxvals) {}
static PointList createByDistance(float maxdist) {}

Envisagez d'utiliser les true typedefs . C'est un peu plus d'effort de la part de votre code client, mais l'exactitude est garantie.

Appelez PointList (10) pour le premier et PointList (10f) pour le second.

Pour le second, vous pouvez également utiliser 10.0.

Si les constructeurs n ° 1 et n ° 2 sont présents, le constructeur approprié sera appelé si la valeur que vous insérez est de type float ou int et aucune conversion ne doit avoir lieu. Assurez-vous simplement que les types de nombres que vous utilisez soient explicites (c'est-à-dire 1f et 1). Le constructeur n ° 3 ne semble pas être vraiment une option, car ce n’est pas vraiment nécessaire et ne ferait que dérouter les utilisateurs de votre code. Si vous avez besoin de valeurs par défaut pour l'un ou l'autre nombre, vous pouvez utiliser

PointList(int max, float max=VALUE)

et

PointList(float max, int max=VALUE)

Encore une fois: cela semble faire plus de mal que le code en termes de lisibilité du code.

Ceci appelle à une bonne lecture sur Résolution de surcharge .

J'utiliserais certainement des constructeurs explicites . Dans l'exemple, l'entier non signé ne convertit pas implicitement.

class A
{
public:
    explicit A(float f){}
    explicit A(int i){}
};

void test(){
    unsigned int uinteger(0);
    A a1(uinteger);        //Fails, does not allow implicit conversions

    A a2((float)uinteger); //OK, explicit conversion

    float f(0.0);
    A a3(f);               //OK

    int integer(0);
    A a4(integer);         //OK
}

Le message d'erreur est assez facile à comprendre:

: error C2668: 'A::A' : ambiguous call to overloaded function
: could be 'A::A(int)'
: or       'A::A(float)'
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top