5

What's wrong with the following snippet ?

#include <tr1/functional>
#include <functional>
#include <iostream>

using namespace std::tr1::placeholders;

struct abc
{
    typedef void result_type;

    void hello(int)
    { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    void hello(int) const
    { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    abc()
    {}
};

int
main(int argc, char *argv[])
{
    const abc x;
    int a = 1;

    std::tr1::bind(&abc::hello, x , _1)(a);
    return 0;
}

Trying to compile it with g++-4.3, it seems that cv-qualifier overloaded functions confuse both tr1::mem_fn<> and tr1::bind<> and it comes out the following error:

no matching function for call to ‘bind(<unresolved overloaded function type>,...

Instead the following snippet compiles but seems to break the const-correctness:

struct abc
{
    typedef void result_type;

    void operator()(int)
    { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    void operator()(int) const
    { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    abc()
    {}
};

...

    const abc x;
    int a = 1;
    std::tr1::bind( x , _1)(a);

Any clue?

Nicola Bonelli
  • 8,101
  • 4
  • 26
  • 35

3 Answers3

4

The lookup is done at a time when the constness of this is not known. You just have to give it a hint via casting. Try this:

typedef void (abc::*fptr)(int) const; // or remove const
std::tr1::bind((fptr)&abc::hello, x , _1)(a);

You may also notice here that removing the const still works. This is because you should be passing x by pointer (because the first argument to a C++ member function, the implicit this parameter, is always a pointer). Try this instead:

typedef void (abc::*fptr)(int) const; // won't compile without const (good!)
std::tr1::bind((fptr)&abc::hello, &x , _1)(a);

As discovered during within my comments below, if you omit the & as you originally did, you'll be passing x by value, which is usually not what you want (though it makes little practical difference in your particular example). This actually seems like an unfortunate pitfall for bind.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • shouldn't bind be expected to evaluate the constness of the object passed as first argument, x in this case ? And what about the second snippet ? – Nicola Bonelli Nov 21 '08 at 22:08
  • I don't know. `bind` is making the call succeed when you pass by value (reference?) and the actual method takes a pointer, but it's not handling constness correctly for some reason. I don't know why this is. If someone had a gun to my head and made me guess, I'd mention "The Forwarding Problem." – John Zwinck Nov 21 '08 at 22:12
  • Aha! It's making a copy when you bind and don't pass by pointer. I will update my answer. – John Zwinck Nov 21 '08 at 22:14
  • std::tr1::bind(std::tr1::ref(x) , _1)(a); solves the clue as well :-) – Nicola Bonelli Nov 21 '08 at 22:20
  • because of this http://www.open-std.org/Jtc1/sc22/wg21/docs/cwg_active.html#547 , you cannot do tr1::bind(tr1::mem_fn(&abc::hello), _1); yet -.- let's hope it gets into c++1x – Johannes Schaub - litb Nov 22 '08 at 15:19
1

As John suggested, the problems arisen in those snippets are the following:

  1. When passing a member-function-pointer it's necessary to specify its signature (if overloaded)
  2. bind() are passed arguments by value.

The first problem is solved by casting the member function pointer provided to bind:

    std::tr1::bind(static_cast< void(abc::*)(int) const >(&abc::hello), x, _1)(a);

The second can be solved by passing the callable object by address (as John suggested), or by means of TR1 reference_wrapper<> -- otherwise it will be passed by value, making the const-correctness breaking hallucination.

Given x a callable object:

std::tr1::bind( std::tr1::ref(x) , _1)(a);

bind() will forward a to the proper operator() in accordance to the x constness.

Nicola Bonelli
  • 8,101
  • 4
  • 26
  • 35
1

This question has been answered, but I find the best way to specify an overload with bind is to specify it on the template:

std::tr1::bind<void(foo::*)(int)>(&foo::bar);

This method is just as explicit, but shorter than casting (with static_cast anyway. But it's cleaner than the C-cast, which is the same length.

coppro
  • 14,338
  • 5
  • 58
  • 73