Как перебирать 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 ищет память, в которой он создает копию.