views:

287

answers:

1

What's wrong with the following little program that passes a function object?

#include <iostream>
#include <functional>

void foo(const std::unary_function<const std::string&, void>& fct) {
  const std::string str = "test";
  fct(str); // error
}

class MyFct : public std::unary_function<const std::string&, void> {
public:
  void operator()(const std::string& str) const {
    std::cout << str << std::endl;
  }
};

int main(int argc, char** argv){
  MyFct f;
  foo(f);
  return 0;
}

I'm getting the following error in line 6:

 no match for call to 
`(const std::unary_function<const std::string&, void>) (const std::string&)'
+8  A: 

A common mistake. unary_function and binary_function are just two structs that add typedefs

argument_type
result_type

and respectively

first_argument_type
second_argument_type
result_type

Not more. They are for convenience of creators of function object types, so they don't have to do those themselves. But they don't behave polymorphic. What you want is function object wrapper. boost::function comes to mind:

void foo(boost::function<void(const std::string&)> const& fct) {
  const std::string str = "test";
  fct(str); // no error anymore
}

Or make it a template

template<typename FunctionObject>
void foo(FunctionObject const& fct) {
  const std::string str = "test";
  fct(str); // no error anymore
}

You can take it by value and then return the copy from foo if use it to apply it to some sequence. Which would allow the function object to update some state variables among its members. for_each is an example that does it like that. Generally, anyway, i would accept them by value because they are usually small and copying them allows greater flexibility. So i do

template<typename FunctionObject>
void foo(FunctionObject fct) {
  const std::string str = "test";
  fct(str); // no error anymore
}

You will then be able to take a copy of fct and save it somewhere, and fct's operator() can be non-const and update some members (which is part of the whole point of operator()). Remember if you take a function object by const reference, you can't generally copy it, because the user could have passed a function. Copying it then will try to locally declare a function instead of a local function pointer. However, accepting by-value will accept a function pointer instead when a function was passed, which can safely be copied.

Johannes Schaub - litb
Thanks for your answer! Is the boost solution as efficient as the template solution?
the boost solution is not as efficient, because it has to make an indirect call to the wrapped function object (it makes its own vtable for that). But it has the advantage that you don't need a template. you can just use that boost.function and store it somewhere (e.g as class member).
Johannes Schaub - litb
i think it comes closest to what you looked for with unary_function
Johannes Schaub - litb