tags:

views:

175

answers:

3
#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()));
}

The preceeding code does not compile. In standard C++ fashion the error output is verbose and difficult to understand. The key part seems to be this...

/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 >]()’

...which I interpet to mean that the assignment operator needed is not defined. I took a look at the source code for insert_iterator and noted that it has overloaded the assignment operator. The copy algorithm must uses the insert iterators overloaded assignment operator to do its work(?).

I guess that because my input iterator is on a container of strings and my output iterator is on a container of MyContainers that the overloaded insert_iterator assignment operator can no longer work.

This is my best guess, but I am probably wrong.

So, why exactly does this not work and how can I accomplish what I am trying to do?

+3  A: 

You need to add:
1. Constructor that takes string (you are trying to add string to container that can contain MyContainer objects).
2. bool operator < (set uses it by default to compare elements)
For instance :

class MyContainer
{
  public:
   MyContainer(const string& v):value(v){};
};
bool operator <(const MyContainer &c1, const MyContainer &c2)
{
return c1.value <c2.value;
}
a1ex07
+4  A: 

What would work would be using the constructor (which would make more sense instead of the assignment):

class MyContainer {
public:
 string value;

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

Then the second problem is that set also requires its contents to be comparable.

As to the cause, insert_iterator works by overloading operator=:

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

As you can see, the righthand value must be either the value type of the container or implicitly convertible to it, which is exactly what a (non-explicit) constructor achieves and the assignment operator doesn't.


Technically you could also make it work without changing the class (e.g if you don't want an non-explicit constructor) by providing a suitable conversion function:

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

which can be used with std::transform:

transform(strings.begin(), strings.end(), inserter(containers, containers.end()), from_string);
UncleBens
Thank you!!!!!!!
`std::set` needs `operator<()`
wilhelmtell
Strictly speaking, it doesn't. It is a side issue here, and briefly mentioned in the answer.
UncleBens
+1  A: 

The problem is twofold:

  1. You're trying to fill a set of MyContainer objects
  2. ... from a list of string objects.

The copy() algorithm tries to convert each string object to a MyContainer object. In C++ to add to class MyContainer conversion support from type string to type MyContainer you need to add a constructor that takes a parameter of type string:

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

private:
    string s;
};

You don't need an assignment operator, because the compiler can get the copying done by the copy-constructor: convert a string to a MyContainer and then use the default assignment operator to assign one MyContainer object onto the other. You will, however need an operator<() because C++ sets are sorted.

wilhelmtell