Как перебирать STL-карту, полную строк в С++
У меня есть следующая проблема, связанная с итерацией по ассоциативному массиву строк, определенному с помощью std:: map.
-- snip --
class something
{
//...
private:
std::map<std::string, std::string> table;
//...
}
В конструкторе я заполняю таблицу парами строковых ключей, связанных с строковыми данными. В другом месте у меня есть метод toString, который возвращает строковый объект, содержащий все ключи и связанные с ним данные, содержащиеся в объекте таблицы (в виде ключа = формат данных).
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());
//....
}
//...
}
Когда я пытаюсь скомпилировать, я получаю следующее
error: "error: no match for call to" (std:: basic_string, std:: allocator > )() ".
Может кто-нибудь объяснить мне, чего не хватает, что я делаю неправильно?
Я только нашел некоторое обсуждение подобной проблемы в случае hash_map, где пользователь должен определить функцию хеширования, чтобы иметь возможность использовать hash_map с объектами std::string. Может быть, что-то похожее и в моем случае?
Спасибо!
Ответы
Ответ 1
Основная проблема заключается в том, что вы вызываете метод с именем first()
в итераторе. То, что вы должны сделать, это использовать свойство first
:
...append(iter->first) rather than ...append(iter->first())
В качестве стиля вы не должны использовать new
для создания этой строки.
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 указал (в комментариях), что в современном С++ вы можете переписать цикл
for (auto& item: table) {
...
}
Ответ 2
-
Не пишите метод toString()
. Это не Java. Внедрите оператор потока для своего класса.
-
Предпочитаете использовать стандартные алгоритмы для написания собственного цикла. В этой ситуации std::for_each()
обеспечивает приятный интерфейс к тому, что вы хотите сделать.
-
Если вы должны использовать цикл, но не собираетесь изменять данные, предпочитайте const_iterator
over iterator
. Таким образом, если вы случайно попытаетесь изменить значения, компилятор предупредит вас.
Тогда:
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));
}
Затем, когда вы хотите его распечатать, просто поместите объект:
int main()
{
something bob;
std::cout << bob;
}
Если вам действительно нужно строковое представление объекта, вы можете использовать lexical_cast
.
int main()
{
something bob;
std::string rope = boost::lexical_cast<std::string>(bob);
}
Детали, которые необходимо заполнить.
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 << value.first << "=" << value.second << "\n";
}
private: std::ostream& m_str;
};
DataMap table;
public:
void something::print(std::ostream& str);
};
Ответ 3
Измените свои добавления для добавления
...append(iter->first)
и
... append(iter->second)
Кроме того, строка
std::string* strToReturn = new std::string("");
выделяет строку в куче. Если вы намерены фактически вернуть указатель на эту динамически выделенную строку, возврат должен быть изменен на std::string *.
В качестве альтернативы, если вы не хотите беспокоиться об управлении этим объектом в куче, измените локальную декларацию на
std::string strToReturn("");
и измените "добавление" вызовов на использование ссылочного синтаксиса...
strToReturn.append(...)
вместо
strToReturn->append(...)
Помните, что это построит строку в стеке, а затем скопирует ее в возвращаемую переменную. Это имеет последствия для производительности.
Ответ 4
iter->first
и iter->second
являются переменными, которые вы пытаетесь назвать их как методы.
Ответ 5
Обратите внимание, что результатом разыменования std:: map:: iterator является std:: pair. Значения first
и second
не являются функциями, они являются переменными.
Изменить:
iter->first()
к
iter->first
То же самое с iter->second
.
Ответ 6
Использование:
std::map<std::string, std::string>::const_iterator
вместо:
std::map<std::string, std::string>::iterator
Ответ 7
в С++ 11 вы можете использовать
for ( auto iter : table ) {
key=iter->first();
value=iter->second();
}
Ответ 8
Еще одна достойная оптимизация - это член c_str() классов STL string, который возвращает неизменяемую строку с нулевым завершением, которая может передаваться как LPCTSTR, например. например, к пользовательской функции, ожидающей LPCTSTR. Хотя я не проследил через деструктор, чтобы подтвердить его, я подозреваю, что класс string ищет память, в которой он создает копию.