Le problème suivant concerne l'itération sur un tableau associatif de chaînes défini à l'aide de std::map
.
-- snip --
class something
{
//...
private:
std::map<std::string, std::string> table;
//...
}
Dans le constructeur, je remplis une table avec des paires de clés de chaîne associées aux données de chaîne. Ailleurs, j'ai une méthode toString
qui retourne un objet chaîne contenant toutes les clés et les données associées contenues dans l'objet table (sous forme de clé = format de données).
std::string something::toString()
{
std::map<std::string, std::string>::iterator iter;
std::string* strToReturn = new std::string("");
for (iter = table.begin(); iter != table.end(); iter++) {
strToReturn->append(iter->first());
strToReturn->append('=');
strToRetunr->append(iter->second());
//....
}
//...
}
Lorsque j'essaie de compiler, j'obtiens l'erreur suivante:
error: "error: no match for call to ‘(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >) ()’".
Quelqu'un pourrait-il m'expliquer ce qui manque, ce que je fais mal? J'ai seulement discuté d'un problème similaire dans le cas de hash_map
où l'utilisateur doit définir une fonction de hachage pour pouvoir utiliser hash_map
avec des objets std::string
. Pourrait être quelque chose de similaire aussi dans mon cas?
Votre principal problème est que vous appelez une méthode appelée first()
dans l'itérateur. Ce que vous êtes censé faire est d'utiliser la propriété appelée first
:
...append(iter->first) rather than ...append(iter->first())
En ce qui concerne le style, vous ne devriez pas utiliser new
pour créer cette chaîne.
std::string something::toString()
{
std::map<std::string, std::string>::iterator iter;
std::string strToReturn; //This is no longer on the heap
for (iter = table.begin(); iter != table.end(); ++iter) {
strToReturn.append(iter->first); //Not a method call
strToReturn.append("=");
strToReturn.append(iter->second);
//....
// Make sure you don't modify table here or the iterators will not work as you expect
}
//...
return strToReturn;
}
edit: facildelembrar a souligné (dans les commentaires) que vous pouvez maintenant réécrire la boucle en C++ moderne
for (auto& item: table) {
...
}
N'écrivez pas une méthode toString()
. Ce n'est pas Java. Implémentez l'opérateur de flux pour votre classe.
Préférez utiliser les algorithmes standard plutôt que d'écrire votre propre boucle. Dans cette situation, std::for_each()
fournit une interface intéressante à ce que vous voulez faire.
Si vous devez utiliser une boucle mais que vous n'avez pas l'intention de modifier les données, préférez const_iterator
à iterator
. De cette façon, si vous essayez accidentellement de modifier les valeurs, le compilateur vous en avertira.
Ensuite:
std::ostream& operator<<(std::ostream& str,something const& data)
{
data.print(str)
return str;
}
void something::print(std::ostream& str) const
{
std::for_each(table.begin(),table.end(),PrintData(str));
}
Ensuite, lorsque vous souhaitez l’imprimer, diffusez simplement l’objet:
int main()
{
something bob;
std::cout << bob;
}
Si vous avez réellement besoin d'une représentation sous forme de chaîne de l'objet, vous pouvez alors utiliser lexical_cast
.
int main()
{
something bob;
std::string rope = boost::lexical_cast<std::string>(bob);
}
Les détails à renseigner.
class somthing
{
typedef std::map<std::string,std::string> DataMap;
struct PrintData
{
PrintData(std::ostream& str): m_str(str) {}
void operator()(DataMap::value_type const& data) const
{
m_str << data.first << "=" << data.second << "\n";
}
private: std::ostream& m_str;
};
DataMap table;
public:
void something::print(std::ostream& str);
};
Changez vos appels d'appels pour dire
...append(iter->first)
et
... append(iter->second)
De plus, la ligne
std::string* strToReturn = new std::string("");
alloue une chaîne sur le tas. Si vous souhaitez réellement renvoyer un pointeur sur cette chaîne allouée dynamiquement, le retour doit être remplacé par std :: string *.
Sinon, si vous ne voulez pas vous soucier de la gestion de cet objet sur le tas, changez la déclaration locale en
std::string strToReturn("");
et changez les appels 'append' pour utiliser la syntaxe de référence ...
strToReturn.append(...)
au lieu de
strToReturn->append(...)
Sachez que cela va construire la chaîne sur la pile, puis copier dans la variable de retour. Cela a des conséquences sur les performances.
iter->first
et iter->second
sont des variables, vous essayez de les appeler en tant que méthodes.
Notez que le résultat de la déréférence de std :: map :: iterator est un std :: pair . Les valeurs de first
et second
ne sont pas des fonctions, mais des variables.
Changement:
iter->first()
à
iter->first
Idem avec iter->second
.
Utilisation:
std::map<std::string, std::string>::const_iterator
au lieu:
std::map<std::string, std::string>::iterator
Une autre optimisation intéressante est le membre c_str () de la STL string classes, qui renvoie une chaîne immuable terminée par un caractère null qui peut être transmise sous la forme d'unLPCTSTR, e. g., à une fonction personnalisée qui attend un LPCTSTR. Bien que je ne sois pas passé par le destructeur pour le confirmer, je soupçonne que la classe string gère la mémoire dans laquelle elle crée la copie.
en c ++ 11, vous pouvez utiliser
for ( auto iter : table ) {
key=iter->first();
value=iter->second();
}