views:

835

answers:

5

I have a vector that I want to insert into a set. This is one of three different calls (the other two are more complex, involving boost::lambda::if_()), but solving this simple case will help me solve the others.

std::vector<std::string> s_vector;
std::set<std::string> s_set;
std::for_each(s_vector.begin(), s_vector.end(), s_set.insert(boost::lambda::_1));

Unfortunately, this fails with a conversion error message (trying to convert boost::lambda::placeholder1_type to std::string).

So... what's wrong with this?

+1  A: 

I think part of the problem is that for_each() is expecting a functor and you're passing it the result of a function call. So your code would call vector<string>::insert() first and then pass the result of that call to for_each(). I'm not sure of the exact syntax, but I think you want to use bind in combination with lambda here. e.g.

for_each(s_vector.begin(), s_vector.end(),
         boost::bind(set<string>::insert, s_set, boost::lambda::_1));
Ferruccio
A: 

Unfortunately, this:

std::for_each(s_vector.begin(), s_vector.end(),
        lambda::bind(&std::set<std::string>::insert, s_set, lambda::_1));

doesn't work. (Note that I used set::insert, because that's what s_set is.) The error is really nasty, but boils down to the fact that it can't figure out which set::insert to use, since there's three overloads. The one that I'm trying to use is the set that will return pair::iterator, bool> (the value insert). Obviously, that's not working.

I noticed you used boost::bind, not boost::lambda::bind -- was that intentional? (They appear to work a bit differently.)

I think you're right about expecting a functor as opposed to a function call result; I'm sure this can be turned into a functor, but my brain is not seeing the answer right now.

Thanks.

Austin Ziegler
I didn't know about boost::lambda::bind. I haven't used boost::lambda much.
Ferruccio
+3  A: 

The error is really nasty, but boils down to the fact that it can't figure out which set::insert to use, since there's three overloads.

You can work around the ambiguity by giving bind a helpful hand, by specifying a pointer to the function you wish to use:

typedef std::set<std::string> s_type;
typedef std::pair<s_type::iterator, bool>(s_type::*insert_fp)(const s_type::value_type&);
std::for_each(s_vector.begin(), s_vector.end(), boost::bind(static_cast<insert_fp>(&s_type::insert), &s_set, _1));

It's not pretty, but it should work.

Mic
I was trying to do that, but not finding the right combination of typedefs and static casts to use. That looks like that will do exactly what I want, although I will probably use a slightly simpler solution similar to what Alastair provided above. This is one of the underbellies of boost::lambda. :(
Austin Ziegler
+2  A: 

I would use a for-loop :-D

Indeed, many would. However, for_each makes it easier to change the data structure later.
Tom Leys
best answer :) upvote for that
Johannes Schaub - litb
+2  A: 

To just copy the vector into the set, you could use std::copy and an insertion iterator. Something like:

std::copy(s_vector.begin(), s_vector.end(), std::inserter(s_set, s_set.end()));

Of course this doesn't use boost::lambda at all, so it probably doesn't help you generalise this into doing what you want. It would be better to know more about what you're trying to do here. I'm going to assume, based on your mention of lambda::_if, that your lambda is going to do some kind of filtering of the input vector before inserting into the set.

The following (complete, tested) example shows how to copy only strings which are <= 4 characters from the vector into the set:

#include <boost/assign/list_of.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/test/minimal.hpp>

#include <set>
#include <vector>
#include <algorithm>

using namespace std;
using namespace boost::lambda;
using namespace boost::assign;

int test_main(int argc, char* argv[])
{
    vector<string> s_vector = list_of("red")("orange")("yellow")("blue")("indigo")("violet");
    set<string> s_set;

    // Copy only strings length<=4 into set:

    std::remove_copy_if(s_vector.begin(), s_vector.end(), std::inserter(s_set, s_set.end()),
                        bind(&string::length, _1) > 4u);

    BOOST_CHECK(s_set.size() == 2);
    BOOST_CHECK(s_set.count("red"));
    BOOST_CHECK(s_set.count("blue"));

    return 0;
}

Hopefully this gives you something to go on?

Also let me reiterate the point made above that boost::bind and boost::lambda::bind are two different beasts. Conceptually they are similar, but they produce outputs of different types. Only the latter can be combined with other lambda operators.

Alastair
As you surmised, my actual solution is doing to be doing some positive match filtering on insertion; I'm also dealing with objects that are significantly heavier than string -- but I think this puts me on the right track.
Austin Ziegler