Вопрос С++ STL, связанный с итераторами вставки и перегруженными операторами

#include <list>
#include <set>
#include <iterator>
#include <algorithm>

using namespace std;

class MyContainer {
public:
 string value;

    MyContainer& operator=(const string& s) {
        this->value = s;
        return *this;
    }
};

int main() 
{
    list<string> strings;
    strings.push_back("0");
    strings.push_back("1");
    strings.push_back("2");

    set<MyContainer> containers;
    copy(strings.begin(), strings.end(), inserter(containers, containers.end()));
}

Предыдущий код не компилируется. В стандартном стиле C++ вывод ошибок многословен и сложен для понимания. Ключевая часть, кажется, это...

/usr/include/c++/4.4/bits/stl_algobase.h:313: error: no match for ‘operator=’ in ‘__result.std::insert_iterator::operator* [with _Container = std::set, std::allocator >]() = __first.std::_List_iterator::operator* [with _Tp = std::basic_string, std::allocator >]()’

... что я интерпретирую, чтобы означать, что необходимый оператор присваивания не определен. Я взглянул на исходный код для insert_iterator и заметил, что он перегружает оператор присваивания. Алгоритм копирования должен использовать перегруженный оператор присваивания итераторов вставки для выполнения своей работы (?).

Я предполагаю, что, поскольку мой итератор ввода находится в контейнере строк, а мой итератор вывода находится в контейнере MyContainers, перегруженный оператор присваивания insert_iterator больше не может работать.

Это мое лучшее предположение, но я, вероятно, ошибаюсь.

Итак, почему именно это не работает и как я могу выполнить то, что пытаюсь сделать?


person rshepherd    schedule 21.04.2010    source источник


Ответы (3)


Что бы сработало, так это использование конструктора (что имело бы больше смысла вместо присваивания):

class MyContainer {
public:
 string value;

    MyContainer(const string& s): value(s) {
    }
};

Тогда вторая проблема заключается в том, что множество также требует, чтобы его содержимое было сравнимо.

Что касается причины, то insert_iterator работает, перегружая operator=:

insert_iterator<Container>& operator= (typename Container::const_reference value);

Как видите, правое значение должно быть либо типом значения контейнера, либо неявно конвертируемым в него, что достигается с помощью (неявного) конструктора, а не с помощью оператора присваивания.


Технически вы также можете заставить его работать без изменения класса (например, если вам не нужен неявный конструктор), предоставив подходящую функцию преобразования:

MyContainer from_string(const std::string& s)
{
    MyContainer m;
    m = s;    //or any other method how to turn a string into MyContainer
    return m;
}

который можно использовать с std::transform:

transform(strings.begin(), strings.end(), inserter(containers, containers.end()), from_string);
person UncleBens    schedule 21.04.2010
comment
std::set нуждается operator<() - person wilhelmtell; 22.04.2010
comment
Строго говоря, это не так. Здесь это побочный вопрос, и он кратко упоминается в ответе. - person UncleBens; 22.04.2010

Вам нужно добавить:
1. Конструктор, который принимает строку (вы пытаетесь добавить строку в контейнер, который может содержать объекты MyContainer).
2. Логический оператор ‹ (set использует его по умолчанию для сравнения элементов)
Например:

class MyContainer
{
  public:
   MyContainer(const string& v):value(v){};
};
bool operator <(const MyContainer &c1, const MyContainer &c2)
{
return c1.value <c2.value;
}
person a1ex07    schedule 21.04.2010

Проблема двоякая:

  1. Вы пытаетесь заполнить набор из MyContainer объектов
  2. ... из списка string объектов.

Алгоритм copy() пытается преобразовать каждый объект string в объект MyContainer. В C++, чтобы добавить в класс MyContainer поддержку преобразования из типа string в тип MyContainer, вам нужно добавить конструктор, который принимает параметр типа string:

struct MyContainer {
    MyContainer(const string& s) : value(s) { }
    bool operator<(const MyContainer& o) const { return value < o.value; }

private:
    string s;
};

Вам не нужен оператор присваивания, потому что компилятор может выполнить копирование с помощью конструктора копирования: преобразовать string в MyContainer, а затем использовать оператор присваивания по умолчанию, чтобы присвоить один объект MyContainer другому. Однако вам понадобится operator<(), потому что наборы C++ отсортированы.

person wilhelmtell    schedule 21.04.2010