Méthode factory renvoyant une instanciation concrète d'une classe de modèle C ++

StackOverflow https://stackoverflow.com/questions/498757

  •  20-08-2019
  •  | 
  •  

Question

j'ai une classe

template <unsigned int N>
class StaticVector {
// stuff
};

Comment déclarer et définir dans cette classe une méthode fabrique statique renvoyant un StaticVector < 3 > objet, comme ça

StaticVector<3> create3dVec(double x1, double x2, double x2);

?

Était-ce utile?

La solution

& "Comment puis-je déclarer et définir dans cette classe &";

Dans quelle classe? Vous avez défini un modèle de classe, pas une classe. Vous ne pouvez pas appeler une fonction statique d’un modèle de classe, vous devez appeler une version particulière de la fonction statique qui fait partie d’une classe réelle.

Alors, voulez-vous que le modèle (et par conséquent toutes ses instanciations) ait une fonction renvoyant un StaticVector < 3 > ;, ou voulez-vous qu'une instanciation particulière de ce modèle ait un fonction renvoyant un StaticVector < 3 >?

Si l'ancien:


  template <unsigned int N>
  struct SV {
    int contents[N];
    static SV<3> get3dVec(int x, int y, int z) {
      SV<3> v;
      v.contents[0] = x;
      v.contents[1] = y;
      v.contents[2] = z;
      return v;
    }
  };

  int main() {
    SV<3> v = SV<1>::get3dVec(1,2,3);
  }

travaille pour moi.

Si ce dernier (vous voulez seulement que get3dVec soit membre de SV < 3 > ;, pas de tous les SV < peu importe >), alors vous voulez un template spécialisation:


  template <unsigned int N>
  struct SV {
    int contents[N];
  };

  template<>
  struct SV<3> {
    int contents[3]; // must be re-declared in the specialization
    static SV<3> get3dVec(int x, int y, int z) {
      SV<3> v;
      v.contents[0] = x;
      v.contents[1] = y;
      v.contents[2] = z;
      return v;
    }
  };

  int main() {
    SV<3> v = SV<1>::get3dVec(1,2,3); // compile error
    SV<3> v = SV<3>::get3dVec(1,2,3); // OK
  }

Si rien d'autre que de rendre le code de l'appelant plus esthétique en omettant le paramètre de modèle fondamentalement non pertinent, je suis d'accord avec Iraimbilanja pour dire qu'une fonction libre (dans un espace de noms si vous écrivez pour une réutilisation) en ferait davantage. sens pour cet exemple.

Les modèles C ++ signifient que vous n’avez pas besoin de fonctions statiques aussi bien en C ++ qu'en Java: si vous voulez un " foo " fonction qui fait une chose pour la classe Bar et une autre chose pour la classe Baz, vous pouvez le déclarer comme modèle de fonction avec un paramètre de modèle qui peut être Bar ou Baz (et qui peut ou non être déduit des paramètres de la fonction), plutôt que de faire c'est une fonction statique sur chaque classe. Mais si vous voulez que ce soit une fonction statique, vous devez l'appeler en utilisant une classe spécifique, pas seulement un nom de modèle.

Autres conseils

Quelque chose comme:

template< unsigned int SIZE >
StaticVector< SIZE > createVec( const std::tr1::array< double, SIZE >& values )
{ 
     StaticVector< SIZE > vector;
     for( int i = 0; i < values.size(); ++i ) // here assuming that StaticVector have [] operator to access values on write
     {
         vector[i] = values[i];
     }

     return vector;
}

... ou une variante fonctionnerait certainement. (je ne l'ai pas testé)

L'utilisation serait:

std::tr1::array< double, 3 > vectorValues = { 10.0, 10.0, 42.0 };

StaticVector<3> vector1 = createVector( vectorValues ); // if the compiler can guess the size from the array information
StaticVector<3> vector2 = createVector<3>( vectorValues ); // if you have to specify the size

Une alternative serait de remplacer std :: tr1 :: array par un tableau de base, mais que vous utiliseriez des tableaux bruts à vos risques et périls:)

Tout d'abord, je pense que vous vouliez à l'origine revenir

StaticVector<N>

Au lieu de toujours une spécialisation avec N == 3. Alors, ce que vous voulez faire, c'est l'écrire comme ceci:

template <unsigned int N>
class StaticVector {
public:
    // because of the injected class-name, we can refer to us using
    // StaticVector . That is, we don't need to name all template
    // parameters like StaticVector<N>.
    static StaticVector create3dVec(double x1, double x2, double x2) {
        // create a new, empty, StaticVector
        return StaticVector();
    }

};

Si vous voulez vraiment renvoyer toujours un 3dVector, vous voudrez probablement le limiter à N == 3, afin que, par exemple, StaticVector<4>::create3dVec ne fonctionne pas. Vous pouvez le faire en utilisant la technique décrite ici .

Si vous souhaitez une fonction comme createVec qui fonctionne avec n’importe quelle taille, vous voudrez probablement remplacer les paramètres par un tableau. Vous pouvez faire autrement, mais cela est avancé et nécessite quelques astuces de macro appliquées avec boost :: preprocessor. Ça n'en vaut pas la peine je pense. La prochaine version C ++ fournira des modèles variadiques à cette fin. Quoi qu'il en soit, envisagez d'utiliser quelque chose comme ceci:

Je pense que cela ne ferait que compliquer inutilement la situation ici. Une solution rapide consiste à utiliser un boost :: fusion :: vector à la place, en le plaçant dans le modèle de classe au lieu de la version ci-dessus:

static StaticVector createVec(double (&values)[N]) {
    // create a new, empty, StaticVector, initializing it
    // with the given array of N values.
    return StaticVector();
}

Vous pouvez l'utiliser avec

double values[3] = {1, 2, 3};
StaticVector<3> v = StaticVector<3>::createVec(values);

Notez qu'il accepte un tableau par référence. Vous ne pouvez pas lui donner un pointeur. En effet, cela correspond à l'utilisation de paramètres: vous ne pouviez pas fournir moins d'arguments, ni plus, pour l'inverse. Cela vous protégera également contre des cas comme celui-ci:

// oops, values is a null pointer!
StaticVector<3> v = StaticVector<3>::createVec(values);

Un tableau ne peut jamais être un pointeur nul. Bien sûr, si vous le souhaitez, vous pouvez toujours modifier le paramètre de tableau en pointeur. Ce serait juste ma préférence personnelle:)

@litb

Je voulais renvoyer un vecteur à 3 éléments. La raison en est que ces vecteurs sont supposés être des entités géométriques, il n'est donc pas nécessaire que N soit trop élevé (je ne suis pas un physicien des cordes, je ne travaille donc pas dans des espaces à 11 dimensions). Je souhaitais créer un modèle pour éviter la duplication de code, mais conserver les vecteurs à 1, 2 et 3 dimensions sous forme de types séparés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top