I've been trying to use find_if() to search a map for an element whose value
is equal to a particular number. I'm trying to use a member function as the
predicate, but I can't get the code to compile. I've attached code that
illustrates the problem. The error I get from g++ is that the mem_fun object
isn't a valid operator for use by bind1st. Can some one point out how to fix this?
I know that I can do this using a global predicate function, a static member
function and even a functor. I can also use a for loop instead of find_if()
and just test for equality within the body of the for loop. I've gotten all of
those methods to work. So I've got plenty of "other solutions" to this
problem, I'm only interested in what I need to do to get this solution to work
or in finding out why it won't work. Thanks.
Tony Richardson
====================================================
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
class test {
public:
void run();
void find11();
bool g4(map<int, int>::value_type &v);
private:
map<int, int> m;
};
int main()
{
test *t = new test;
t->run();
return 0;
}
void test::run() { m[1] = 5; m[3] = 7; m[5] = 11; m[7] = 13; find11(); }
// non-static member
bool test::g4(map<int, int>::value_type &v) { return v.second == 11; }
void test::find11()
{
map<int, int>::iterator i;
// the following line will not compile
i = find_if(m.begin(), m.end(), bind1st(mem_fun(&test::g4), this));
if(i != m.end())
cout << "(key, value) = (" << i->first << ", " << i->second <<
")" << endl;
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
The function objects binder1st<> and binder2nd<> can't really handle
functions that take reference parameters. Your mem_fun expression
returns a functor that has a nested typedef for second_argument_type
which is a reference in your case and binder1st tries to create
functions which take parameters of type "second_argument_type&" and
"const second_argument_type&" (overloaded function call operator).
This is obviously not going to work (reference to reference). Even
considering the new reference collapsing rules of C++0x this results
in a pair of functions that are equivalent and cannot be overloaded
(as far as I can tell).
Solution: Use (boost::, std::tr1::) bind instead or write your own
functor for this.
Cheers!
SG
That is an interesting problem, that points to some
weaknesses in the binder framework of C++03 *and* in
their revised (but deprecated) forms of C++0x, see below.
The actual fix depends on which version of gcc you are
using and which compiler flags you use - ouch!
> ====================================================
> #include <iostream>
> #include <map>
> #include <algorithm>
Missing header
#include <functional>
because you are using components from this header
(mem_fun, bind1st). I assume that adding this does
not solve your problem, so read on.
> using namespace std;
>
> class test {
> public:
> void run();
> void find11();
> bool g4(map<int, int>::value_type &v);
> private:
> map<int, int> m;
> };
>
> int main()
> {
> test *t = new test;
> t->run();
> return 0;
> }
The usage of free store unnecessary complicates your example.
test t; t.run();
are sufficient to emphasize the problem.
> void test::run() { m[1] = 5; m[3] = 7; m[5] = 11; m[7] = 13; find11(); }
>
> // non-static member
> bool test::g4(map<int, int>::value_type &v) { return v.second == 11; }
>
> void test::find11()
> {
> map<int, int>::iterator i;
> // the following line will not compile
> i = find_if(m.begin(), m.end(), bind1st(mem_fun(&test::g4), this));
> if(i != m.end())
> cout << "(key, value) = (" << i->first << ", " << i->second <<
> ")" << endl;
>
> }
First: Which version of gcc are you using?
Let's analyze what's going on here, assuming
some scenarios:
a) You use an older gcc with C++03 support. In
this scenario the following library components
are involved:
template<class S, class T, class A>
mem_fun1_t<S,T,A> mem_fun(S (T::*f)(A));
which returns:
mem_fun1_t<bool, test, map<int, int>::value_type&>
which is the same as:
mem_fun1_t<bool, test, pair<const int, int>&>
and which provides the following typedefs:
first_argument_type: test*
second_argument_type: pair<const int, int>&
result_type: bool
and the following operator() overload:
bool operator()(test* p, pair<const int, int>& x) const;
Then we have
template <class Operation, class T>
binder1st<Operation> bind1st(const Operation&, const T&);
which returns:
binder1st<mem_fun1_t<bool, test, pair<const int, int>&> >
and which provides the following typedefs:
argument_type: pair<const int, int>&
result_type: bool
and the following operator() overload:
bool operator()(const typename Operation::second_argument_type& x)
const;
In C++98/03 this signature is invalid, because of the
attempt to form a reference of a reference. This became
officially fixed in core issue 106 but several compilers
accepted that before this fix (I assume that this is not
the problem you have). The introduced reference-folding
rules make above declaration equal to:
bool operator()(pair<const int, int>& x) const;
This operator is supposed to invoke
bool operator()(test* p, pair<const int, int>& x) const
of the wrapped mem_fun1_t object. In theory this should
work, but some compilers introduced constraints checks
that verify that the operator could be called with
const value types. You can check whether this is the
case in your example by replacing the declaration
bool g4(map<int, int>::value_type &v);
by
bool g4(const map<int, int>::value_type &v);
Given your example, this is already useful, because
g4 does not modify it's argument (You should also
strongly consider to make the member function a
const function).
It may turn out that this does not fix the problem ;-)
and we have situation:
b) This is probably due to the fact that you have a
*new* gcc which already implements the changes
applied to the binder types as part of library
issue 109. The idea of this issue was to solve
the problem that the binders are designed in a
way that could not handle bound values that
should be provided to a mutable member function.
The suggested fix was to add another overload
of operator(), in general for binder1st
typename Fn::result_type
operator()(const typename Fn::second_argument_type& x) const;
typename Fn::result_type
operator()(typename Fn::second_argument_type& x) const;
Now inserting the deduced types and applying
reference folding rules we end in
bool operator()(pair<const int, int>& x) const;
bool operator()(pair<const int, int>& x) const;
which are identical and which cause the program
to be ill-formed.
The solution given your use-case is to stay away
from the now deprecated old binder classes and
to uses std::bind instead (this requires a new
compiler) or to use boost::bind. More specifically,
replace the current expression
bind1st(mem_fun(&test::g4), this)
by
bind(&test::g4, this, placeholders::_1)
HTH & Greetings from Bremen,
Daniel Krügler
> i = find_if(m.begin(), m.end(), bind1st(mem_fun(&test::g4), this));
You could use boost::bind(&test::g4, this).
This is also included in TR1.
> I've been trying to use find_if() to search a map for an element whose value
> is equal to a particular number. I'm trying to use a member function as the
> predicate, but I can't get the code to compile. I've attached code that
...
> class test {
> public:
> void run();
> void find11();
> bool g4(map<int, int>::value_type &v);
...
> i = find_if(m.begin(), m.end(), bind1st(mem_fun(&test::g4), this));
The problem is in g4 signature that leads to "const argument_type"
deduction and clash between const and non const operator() in
binder1st. The function should take parameter by value to avoid that,
so remove reference and it should compile:
bool g4(map<int, int>::value_type v);