views:

184

answers:

3

Given the following callable object:

struct callable : public std::unary_function <void, void>
{
    void
    operator()() const
    {
        std::cout << "hello world" << std::endl;
    }
};  

a std::tr1::reference_wrapper<> calls through it:

callable obj;
std::tr1::ref(obj)();

Instead, when the operator() accepts an argument:

struct callable : public std::unary_function <int, void>
{
    void
    operator()(int n) const
    {
        std::cout << n << std::endl;
    }
};  

std::tr1::bind accepts a reference_wrapper to it as a callable wrapper...

callable obj;
std::tr1::bind( std::tr1::ref(obj), 42 )();

but what's wrong with this?

std::tr1::ref(obj)(42);

g++-4.4 fails to compile with the following error:

test.cpp:17: error: no match for call to ‘(std::tr1::reference_wrapper<const callable>) (int)’
/usr/include/c++/4.4/tr1_impl/functional:462: note: candidates are: typename std::tr1::result_of<typename std::tr1::_Function_to_function_pointer<_Tp, std::tr1::is_function::value>::type(_Args ...)>::type std::tr1::reference_wrapper<_Tp>::operator()(_Args& ...) const [with _Args = int, _Tp = const callable]
A: 

First of all, the use of std::unary_function for a null-ary function looks odd. "unary" = takes one argument. I'm not sure whether it's okay to use ArgType=void.

Secondly, you have it backwards. The first template parameter is about the argument type and the second one is about the return type. So, your unary function object should be defined like this:

struct callable : public std::unary_function<int,void>
{
    void operator()(int n) const
    {
        std::cout << n << std::endl;
    }
};
sellibitze
I can't see any difference between mine and yours. Template arguments of unary_function are set correctly, and void should be fine for operator()(void). Besides, the problem is for the second one...
Nicola Bonelli
+2  A: 

What makes you sure there's anything wrong with it? I believe this should work:

#include <functional>
#include <iostream>

struct callable : public std::unary_function <int, void>
{
    void
    operator()(int n) const
    {
        std::cout << n << std::endl;
    }
};

int main() {     
    callable obj;
    std::tr1::ref(obj)(42);
    return 0;
}

At least with MS VC++ 9, it compiles and executes just fine, and offhand I can't see any reason it shouldn't work with other compilers as well.

Edit: Doing some looking at TR1, I take that back. It works with VC++ 9, but I don't think it's really required to work. VC++ 9 doesn't support variable template arguments, so they're supporting this via overloading. Rather deeply buried (<functional> includes <xawrap>, which includes <xawrap0> [which, in turn, includes <xawrap1>]) is code to generate reference and (importantly) reference to const variants for up to 10 arguments. It's almost certainly the inclusion of the reference to const variants that allows this to work.

Jerry Coffin
It does not compile with g++-4.3, nor with g++-4.4...
Nicola Bonelli
yes Jerry, const reference can be bound to rvalue. Besides, C++0x standard requires reference_wrapper's operator() to be implemented in term of r-value reference. Instead, g++-4.4 is still implementing it with l-value ref, even when compiling with -std=c++0x. So it is definitely a bug (not really a bug for c++98).
Nicola Bonelli
+2  A: 

The implementation of tr1 reference_wrapper of g++-4.4 is equipped with the following operator:

  template<typename... _Args>
    typename result_of<_M_func_type(_Args...)>::type
    operator()(_Args&... __args) const
    {
      return __invoke(get(), __args...);
    }

It takes arguments by reference. Hence the reference_wrapper cannot be invoked passing an r-value argument:

std::tr1::ref(obj)(42);

instead:

int arg = 42;
std::tr1::ref(obj)(arg);

works just fine.

std::tr1::bind( std::tr1::ref(obj), 42 )() 

works because bind takes the arguments by copy.

Nicola Bonelli