Search code examples
c++constantsfunctorfunction-object

Const and non-const functors


This seems like something that ought to be frequently asked and answered, but my search-fu has failed me.

I'm writing a function which I want to accept a generic callable object of some kind (including bare function, hand-rolled functor object, bind, or std::function) and then invoke it within the depths of an algorithm (ie. a lambda).

The function is currently declared like this:

template<typename T, typename F>
size_t do_something(const T& a, const F& f)
{
   T internal_value(a);
   // do some complicated things
   // loop {
   //   loop {
       f(static_cast<const T&>(internal_value), other_stuff);
       // do some more things
   //   }
   // }
   return 42;
}

I'm accepting the functor by reference because I want to guarantee that it does not get copied on entry to the function, and thus the same instance of the object is actually called. And it's a const reference because this is the only way to accept temporary objects (which are common when using hand-rolled functors or bind).

But this requires that the functor implement operator() as const. I don't want to require that; I want it to be able to accept both.

I know I can declare two copies of this method, one that accepts it as const and one as non-const, in order to cover both cases. But I don't want to do that as the comments are hiding quite a lot of code that I don't want to duplicate (including some loop constructs, so I can't extract them to a secondary method without just moving the problem).

I also know I could probably cheat and const_cast the functor to non-const before I invoke it, but this feels potentially dangerous (and in particular would invoke the wrong method if the functor intentionally implements both const and non-const call operators).

I've considered accepting the functor as a std::function/boost::function, but this feels like a heavy solution to what ought to be a simple problem. (Especially in the case where the functor is supposed to do nothing.)

Is there a "right" way to satisfy these requirements short of duplicating the algorithm?

[Note: I would prefer a solution that does not require C++11, although I am interested in C++11 answers too, as I'm using similar constructs in projects for both languages.]


Solution

  • Have you tried a forwarding layer, to force inference of the qualifier? Let the compiler do the algorithm duplication, through the normal template instantiation mechanism.

    template<typename T, typename F>
    size_t do_something_impl(const T& a, F& f)
    {
       T internal_value(a);
       const T& c_iv = interval_value;
       // do some complicated things
       // loop {
       //   loop {
           f(c_iv, other_stuff);
           // do some more things
       //   }
       // }
       return 42;
    }
    
    template<typename T, typename F>
    size_t do_something(const T& a, F& f)
    {
       return do_something_impl<T,F>(a, f);
    }
    
    template<typename T, typename F>
    size_t do_something(const T& a, const F& f)
    {
       return do_something_impl<T,const F>(a, f);
    }
    

    Demo: http://ideone.com/owj6oB

    The wrapper should be completely inlined and have no runtime cost at all, except for the fact that you might end up with more template instantiations (and therefore larger code size), though that will only happen when for types with no operator()() const where both const (or temporary) and non-const functors get passed.