Как выполнить итерацию по карте STL, полной строк в C++


у меня есть следующая проблема, связанная с итерацией по ассоциативному массиву строк, определенных с помощью 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());
           //....
        }
       //...
}

когда я пытаюсь скомпилировать я получаю следующее

ошибка: "ошибка: не подходит для вызова ‘(СТД::элементах, с std::распределитель >) ()’".

спасибо!

8 62

8 ответов:

ваша главная проблема заключается в том, что вы вызываете метод, называемый 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 указал (в комментариях), что в современном C++ теперь можно переписать цикл

for (auto& item: table) {
    ...
}
  1. не писать toString() метод. Это не Java. Реализуйте оператор потока для вашего класса.

  2. предпочитайте использовать стандартные алгоритмы над написанием собственного цикла. В этой ситуации std::for_each() обеспечивает хороший интерфейс для того, что вы хотите сделать.

  3. если вы должны использовать цикл, но не намерены менять данных, предпочитают 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);
};

измените свои вызовы append, чтобы сказать

...append(iter->first)

и

... append(iter->second)

дополнительно строку

std::string* strToReturn = new std::string("");

выделяет строку в куче. Если вы намереваетесь фактически вернуть указатель на эту динамически выделенную строку, возврат должен быть изменен на std::string*.

кроме того, если вы не хотите беспокоиться об управлении этим объектом в куче, измените локальное объявление на

std::string strToReturn("");

и изменить, "добавить" звонки для использования ссылочного синтаксиса...

strToReturn.append(...)

вместо

strToReturn->append(...)

имейте в виду, что это будет построить строку в стеке, то скопировать это в возвращаемую переменную. Это имеет последствия для производительности.

обратите внимание, что результатом разыменования std::map:: iterator является std:: pair. Значения first и second это не функции, это переменные.

изменения:

iter->first()

до

iter->first

то же самое с iter->second.

iter->first и iter->second являются переменными, вы пытаетесь вызвать их как методы.

использование:

std::map<std::string, std::string>::const_iterator

вместо:

std::map<std::string, std::string>::iterator

в c++11 вы можете использовать

for ( auto iter : table ) {
     key=iter->first();
     value=iter->second();
}

еще одна достойная оптимизация -c_str () член STL string classes, который возвращает неизменяемую строку с нулевым завершением, которая может быть передана как LPCTSTR, например, к пользовательской функции, которая ожидает LPCTSTR. Хотя я не проследил через деструктор, чтобы подтвердить это, я подозреваю, что класс string заботится о памяти, в которой он создает копию.