views:

1131

answers:

3

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?

+1  A: 

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
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
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
Aha! It's making a copy when you bind and don't pass by pointer. I will update my answer.
John Zwinck
std::tr1::bind(std::tr1::ref(x) , _1)(a); solves the clue as well :-)
Nicola Bonelli
because of this http://www.open-std.org/Jtc1/sc22/wg21/docs/cwg_active.html#547 , you cannot do tr1::bind(tr1::mem_fn<void(int) const>( yet -.- let's hope it gets into c++1x
Johannes Schaub - litb
A: 

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
A: 

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