1

Goals

  • A class with a function-typed member variable that can be used to choose a value from a range of values denoted by two iterators
  • Should work in C++14, but if a (much) better solution is only possible in 17 or 20, I'd be interested nonetheless

Problem and Solution Sketch

Below is a sketch of the envisioned solution, with ??? denoting the choose-function's type, which I can't work out.

template<typename E>
class Producer {
  ??? choose_from; // A function It × It → E
  ...
public:
  Producer(??? cf): choose_from{cf} {}

  E next() {
    // Get a bunch of values from internal state (omitted for brevity).
    // Could also be another container, e.g. std::set.
    std::vector<E> values = ...;

    return choose_from(values.begin(), values.end());
  }
}

Next up, a few example usage sketches of such a Producer. A solution that only supports a subset of those would still be fine.

  • In combination with an existing function:

    auto p = Producer<int>(std::max_element)`;
    
  • In combination with a lambda function:

    auto p = Producer<int>([](auto begin, auto end)) { ... });
    
  • In combination with a custom free function:

    template<typename E, typename It>
    E my_selector(It begin, It end) { ... }
    
    auto p = Producer<int>(my_selector);
    

Question

How do I get the code to work, i.e. what's the/a right type for member variable choose_from?

Attempt & Thoughts

  • I've tried to use std::function to type choose_from, but since there is no abstract iterator type (AFAIK), I'm basically stuck at std::function<E(IT?, IT?)>.

  • If the use of std::vector in Producer::next() were leaked, I could fix choose_from's type to handle std::vector<E>::iterators, but I'd much rather keep that internal detail hidden.

Malte Schwerhoff
  • 12,684
  • 4
  • 41
  • 71

1 Answers1

1

std::min_element is a function template, there isn't much you can do with it by itself. Function templates are problematic - they can't be passed as a template argument, nor as function argument.

If you're OK with wrapping it in a generic lambda, then there's a generic solution possible:

template<class E, class F>
class Producer {
    F choose_from; // A function It - It → It

public:
    Producer(F const& cf) : choose_from{ cf } {}

    E next() {
        std::vector<E> values{ 5, 2, 3 };

        return *choose_from(values.begin(), values.end());
    }
};

template<typename E, typename F>
auto make_producer(F const& cf) {
    return Producer<E, F>(cf);
}

template<typename It>
It my_selector(It begin, It end) { return begin; }

int main() {
    auto p1 = make_producer<int>([](auto from, auto to) { return from; });
    cout << p1.next() << endl;
    auto p2 = make_producer<int>([](auto from, auto to) { return std::min_element(from, to); });
    cout << p2.next() << endl;
    auto p3 = make_producer<int>([](auto from, auto to) { return my_selector(from, to); });
    cout << p3.next() << endl;
}

Notes:

  • There's also no partial CTAD, so even in C++17 you'd need a helper function (make_producer() above).

  • std::function comes with run-time overhead (1..2 extra indirections). It should be used only when run-time polymorphism is unavoidable (e.g. dynamic callbacks) -- not the case here. Do not use.

rustyx
  • 80,671
  • 25
  • 200
  • 267