4

class Frame<P> represents an image with pixels of type P. The algorithm that iterates through its pixels is non-trivial due to several flexibilities in the underlying data buffer format.

template <typename P, bool RM = true> // P is pixel type; RM = is_row_major
class Frame {

    // ...

    template<typename F>
    void iterate(F f) { // iterate in a way that is performant for this buffer
        if (stride == (RM ? size.w : size.h)) {
            auto n = size.area();
            for (index_t k = 0; k < n; k++) {
                f(view[k]);
            }
        }
        else {
            auto s = static_cast<index_t>(stride)*(RM ? size.h : size.w);
            for (index_t k0 = 0; k0 < s; k0 += stride) {
                auto m = k0 + (RM ? size.w : size.h);
                for (index_t k = k0; k < m; k++) {
                    f(view[k]);
                }
            }
        }
    }
}

I would like to be able to call both iterate(fc) const and iterate(f) (with lambda signatures fc(const P&) and f(P&) respectively). I could duplicate the entire function body and attach const to the signature, but is there a better way?

Museful
  • 6,711
  • 5
  • 42
  • 68
  • Does this [thread](https://stackoverflow.com/q/856542/2412846) here help? (If not, then I obviously have not understood the question -- could you make it clearer,and possibly minimize your example?) – davidhigh May 05 '18 at 18:09
  • @davidhigh Not sure. I use that regularly on simple getters. Is it safe to use `const_cast` here too? – Museful May 05 '18 at 18:16
  • Use it on what? Frankly, I have not understood the question ... can you give an example in your question of what you want to do, with just two `operator+=` members, one const, one not-const? – davidhigh May 05 '18 at 18:19
  • @davidhigh For me `operator+=` is inherently non-const. What I want is an `iterateTogether() const` without duplicating the entire method. – Museful May 05 '18 at 18:25
  • Why can't you just have one `iterateTogether` function that is `const`? There is no problem calling a `const` member function from a non-const one. – Chris Drew May 05 '18 at 18:43
  • @davidhigh I've simplified the question. – Museful May 05 '18 at 18:45
  • @ChrisDrew But the non-const version of `iterate()` should be able to pass argument to lambda by non-const reference, which the const version of `iterate()` will not allow. – Museful May 05 '18 at 18:50
  • @Museful OK, I see. – Chris Drew May 05 '18 at 18:52

1 Answers1

1

The constness of member functions cannot be deduced. However, using forwarding references allows deducing the costness of function arguments. Thus, you can just delegate to a function taking the object type as argument and forwarding all other arguments. This way the duplicated code becomes trivial. If the function needs access to private or protected members you’d just make it a (probably private ) static member:

template <typename T, bool PM = true>
class Frame {
    template <typename F1, typename F2>
    static void iterateTogether(F1&& f1, F2&& f2) {
        // actual implementation goes here
    }
public:
    template <typename F2>
    void iterateTogether(F2&& other){
        iterateTogether(*this, std::forward<F2>(other));
    }
    // ...
 };

The implementation used above also allows different constness of the parameter. If you want to constrain the parameter to really just be a specialization of Frame you’d need to constrain the function which is, however, easily done.

My personal preference is having only trivial members anyway and delegate anything non-trivial to generic algorithms. My preference is that classes just maintain their invariants and interesting uses are implemented via algorithms.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380