1

This is a specific case of this question where that answer doesn't directly work.

Consider:

struct hurg {};

class furg {
public:
    template <class F>
    void for_each_hurg(F&& f) const {
        for (auto& h : hurgs) {
            f(h);
        }
    }

    template <class F>
    void for_each_hurg(F&& f) {
        for (auto& h : hurgs) {
            f(h);
        }
    }
private:
    std::vector<hurg> hurgs;
};

Usage:

furg f;
const auto& cf = f;

f.for_each_hurg([](hurg& h) { });
cf.for_each_hurg([](const hurg& h) { });

The code for the const and non-const versions is identical, but only because auto& h infers const hurg& in the first case and hurg& in the second case.

In the spirit of the previously-linked-to Scott Meyers' solution, I came up with the following:

template <class F>
void for_each_hurg(F&& f) {
    const_cast<const furg&>(*this).for_each_hurg([&f](const hurg& h) {
        f(const_cast<hurg&>(h));
    });
}    

However, this seems like it could be more trouble than it's worth, especially if the types are long and if I can't use C++14's generic lambdas.

Community
  • 1
  • 1
Claudiu
  • 206,738
  • 150
  • 445
  • 651
  • 1
    You could use a friend function template with perfect forwarding. – dyp Aug 05 '15 at 17:28
  • Would you mind clarifying the difference between your question and the one you've cited? I can see that you're using a (member) function template whereas the other question uses an "ordinary" (member) function; but there are approaches which work for both. – dyp Aug 05 '15 at 17:33
  • @dyp: Ahh [like so](https://ideone.com/i4AVT4)? Basically the top two answers there wouldn't work in this case (because the callback in the non-const version would still be passed const member variables) so I wondered if there was a nicer way to do it than my solution. I like the friend function template approach - I don't think that answer features there. (Actually seems even nicer if I make it a static private function.) – Claudiu Aug 05 '15 at 17:37
  • @dyp Sounds like an answer to me... – Barry Aug 05 '15 at 17:41
  • @Barry On my way, slowly :) – dyp Aug 05 '15 at 17:42

1 Answers1

3

You can use a static member function template to forward *this to a generic function parameter:

template<typename Self, typename F>
static void for_each_hurg(Self& s, F&& f) {
    for (auto& h : s.hurgs) {
        f(h);
    }
}

template<typename F>
void for_each_hurg(F&& f) { for_each_hurg(*this, forward<F>(f))); }

template<typename F>
void for_each_hurg(F&& f) const { for_each_hurg(*this, forward<F>(f))); }

Since the advent of reference qualifiers for member functions, the general solution is to perfectly forward *this. This doesn't always matter, since you often don't want member functions to be called on rvalues anyway. I'll add this here since I think it is part of a more general solution.

Unfortunately, *this is always an lvalue, so you need additional manual care in the member function wrappers:

template<typename Self, typename F>
static void for_each_hurg(Self&& s, F&& f) {
    /* ... */
}

template<typename F>
void for_each_hurg(F&& f) && { for_each_hurg(move(*this), forward<F>(f))); }

template<typename F>
void for_each_hurg(F&& f) & { for_each_hurg(*this, forward<F>(f))); }

Which is unfortunately not symmetric :(


It is also possible to implement the above via a friend function template. This can have two benefits:

  • You can move the friend function template to namespace scope, which in the case of furg being a class template reduces the amount of function templates the compiler has to deal with (one per class template, instead of one per instantiation). This typically requires some boilerplate code and forward-declarations, though.
  • You can call the function with the same name either as a free function or as a member function, e.g. furg f; for_each_hurg(furg, [](hurg){}); furg.for_each_hurg([](hurg){}); (When unqualified lookup finds a member function, it doesn't perform / ignores the results of ADL. Therefore, you'd have to put the friend function in namespace scope, in order to be able to refer to it via a qualified-id from within the non-static member function wrappers.)

Additionally, you'd have to protect that function template from being to greedy; either by putting it into some namespace detail or adding an enable-if clause. It's probably not worth the effort.

dyp
  • 35,820
  • 10
  • 96
  • 156