J'essaie de créer une fonction générique qui supprime les duplicats d'un std :: vecteur. Puisque je ne veux pas créer de fonction pour chaque type de vecteur, je souhaite faire une fonction de modèle pouvant accepter des vecteurs de n'importe quel type. Voici ce que j'ai:
//foo.h
Class Foo {
template<typename T>
static void RemoveVectorDuplicates(std::vector<T>& vectorToUpdate);
};
//foo.cpp
template<typename T>
void Foo::RemoveVectorDuplicates(std::vector<T>& vectorToUpdate) {
for(typename T::iterator sourceIter = vectorToUpdate.begin(); (sourceIter != vectorToUpdate.end() - 1); sourceIter++) {
for(typename T::iterator compareIter = (vectorToUpdate.begin() + 1); compareIter != vectorToUpdate.end(); compareIter++) {
if(sourceIter == compareIter) {
vectorToUpdate.erase(compareIter);
}
}
}
}
//SomeOtherClass.cpp
#include "foo.h"
...
void SomeOtherClass::SomeFunction(void) {
std::vector<int> myVector;
//fill vector with values
Foo::RemoveVectorDuplicates(myVector);
}
Je continue à obtenir une erreur de liaison, mais cela compile bien. Des idées quant à ce que je fais mal?
MISE À JOUR: Sur la base de la réponse donnée par Iaimbilanja, je suis allé et réécrit le code. Cependant, juste au cas où quelqu'un souhaite que le code de travail effectue la fonction d'enlevée, c'est ici:
//foo.h
Class Foo {
template<typename T>
static void RemoveVectorDuplicates(T& vectorToUpdate){
for(typename T::iterator sourceIter = vectorToUpdate.begin(); sourceIter != vectorToUpdate.end(); sourceIter++) {
for(typename T::iterator compareIter = (sourceIter + 1); compareIter != vectorToUpdate.end(); compareIter++) {
if(*sourceIter == *compareIter) {
compareIter = vectorToUpdate.erase(compareIter);
}
}
}
};
Il s'avère que si je spécifie STD :: Vecteur de la signature, les itérateurs ne fonctionnent pas correctement. Je devais donc aller avec une approche plus générique. De plus, lors de l'effacement de la compréation, la prochaine itération de la boucle produit une exception de pointeur. Le post-décrément de comparaison sur une Erase prend soin de ce problème. J'ai également corrigé les bugs dans l'itérateur Comparer et dans l'initialisation de la compréation dans la 2e boucle.
Mise à jour 2:
J'ai vu que cette question a eu un autre vote à un autre, alors figuré que je le tiendrais à la mettre à jour avec un meilleur algorithme qui utilise une bonté C++ 14. Mon précédent a fonctionné uniquement si le type stocké dans le vecteur implémenté opérateur == et nécessite un groupe de copies et de comparaisons inutiles. Et, dans le recul, il n'est pas nécessaire de faire un membre d'une classe. Ce nouvel algorithme permet un prédicat de comparaison personnalisé, réduit l'espace de comparaison comme des doublons se trouvent et constitue un nombre considérable de copies. Le nom a été changé en erase_duplicates
Pour mieux être conforme aux conventions de nommage d'algorithme STL.
template<typename T>
static void erase_duplicates(T& containerToUpdate)
{
erase_duplicates(containerToUpdate, nullptr);
}
template<typename T>
static void erase_duplicates(T& containerToUpdate,
std::function<bool (typename T::value_type const&, typename T::value_type const&)> pred)
{
auto lastNonDuplicateIter = begin(containerToUpdate);
auto firstDuplicateIter = end(containerToUpdate);
while (lastNonDuplicateIter != firstDuplicateIter) {
firstDuplicateIter = std::remove_if(lastNonDuplicateIter + 1, firstDuplicateIter,
[&lastNonDuplicateIter, &pred](auto const& compareItem){
if (pred != nullptr) {
return pred(*lastNonDuplicateIter, compareItem);
}
else {
return *lastNonDuplicateIter == compareItem;
}
});
++lastNonDuplicateIter;
}
containerToUpdate.erase(firstDuplicateIter, end(containerToUpdate));
}
Définissez la fonction dans l'en-tête, de préférence à l'intérieur de la définition de la classe.
Définir la fonction de modèle à l'intérieur du fichier .cpp signifie qu'il n'aura pas #include
D dans toutes les unités de traduction: il ne sera disponible que pour l'unité de traduction qu'il est défini dans.
Par conséquent, RemoveVectorDuplicates
doit être défini dans l'en-tête, car il s'agit de la seule façon de remplacer par SMS-substitut des arguments de modèle, donc Instancitation Le modèle, produisant une classe utilisable.
premier, vous pouvez supprimer le #include "foo.h"
À partir du fichier .cpp et ajoutez un autre, dans le fin du en-tête := :=:
#include "foo.cpp"
Cela vous permet d'organiser vos fichiers de manière cohérente, mais ne fournit pas les avantages habituels de la compilation séparée (plus petites dépendances, plus rapides et plus rares).
Deuxièmement, vous pouvez simplement définir la fonction de modèle dans le fichier .cpp et l'instanciez explicitement pour tous les types que ce sera jamais utilisé.
Par exemple, cela peut aller à la fin de la .CPP pour rendre la fonction utilisable avec int
s:
template void Foo::RemoveVectorDuplicates(std::vector<int>*);
Cependant, cela suppose que vous n'utilisez que des modèles pour sauver une frappe de frappe, plutôt que de fournir une véritable généricité.
Une alternative que vous avez est à première std::sort()
le vecteur, puis utilisez la fonction préexistante std::unique()
_ fonction pour supprimer des doublons. Le genre prend le temps O (Nlog N) et éliminer les doublons après cela prend juste O(n) Durée car tous les doublons apparaissent dans un seul bloc. Votre comparaison actuelle "All-Tous" L'algorithme prend le temps O (n ^ 2).
Vous ne pouvez pas implémenter une fonction de modèle dans un fichier .cpp. La mise en œuvre complète doit être visible n'importe où qu'elle est instanciée.
Définissez simplement la fonction dans la définition de la classe dans l'en-tête. C'est la façon habituelle de mettre en œuvre des fonctions de modèle.
Je suggère d'utiliser une approche plus "générique", au lieu de passer un conteneur, recevez simplement deux itérateurs.
Quelque chose comme ça retirer_duplicates (c'est d'abord le dernier) et retournera un itérateur, vous pouvez donc appeler comme supprimer: v.erase(remove_duplicates(v.begin(), v.end()), v.end())
.
template <typename It>
It remove_duplicate(It first, It last)
{
It current = first;
while(current != last) {
// Remove *current from [current+1,last)
It next = current;
++next;
last = std::remove(next, last, *current);
current = next;
}
return last;
}
Non liée à votre problème (qui a déjà été expliqué), pourquoi est-ce une fonction statique plutôt que de résider globalement dans un espace de noms? Ce serait un peu C++ - IER.
Je ne pense pas que ce code compile ....
vectoroupdate.erase où std :: vecteur * vectorupdate ... Est-ce que quelqu'un d'autre remarque-t-il qu'il y a un * où il devrait y avoir un &? Ce code n'est certainement pas compilé. Si vous allez utiliser un pointeur pour vectoriel, vous devez utiliser '->' au lieu de "." Je sais que c'est en fait un peu malade, mais cela souligne que le compilateur ne se soucie même pas de votre code ...