1

I have a class which takes an input, and sometimes I'd like to set that input by assigning a variable, and at other times I'd like the class to call a function to get its input.

In the past, I'd have just used a std::function<T()> as the input, and set a lambda to return the value of some external variable, but I'm trying to wean off an overuse of std::function. So I came up with std::variant<T, std::function<T()>>:

template <typename T>
using functionable = std::variant<T, std::function<T()>>;

// return the T or the result of the T() from the variant
template <typename T>
T get(const functionable<T>& f) {
  if (f.index() == 0)
    return std::get<0>(f);
  else
    return std::get<1>(f)();
}

Implemented thus:

class SomeClass {
private:
  functionable<int> input_{0};

public:
  SomeClass(const functionable<int>& input) : input_{input} {}

  SomeClass& operator=(const functionable<int>& rhs) {
    input_ = rhs;
    return *this;
  }

  void print() { std::cout << get(input_) << '\n'; }

And used flexibly thus:

SomeClass foo {42}; // init with assigned value
foo.print();

foo = 101;   // overwrite assigned value
foo.print();

bool a{true};

             // replace input value with input lambda
foo { [this]{if(a) return 10; else return 20;} };
foo.print();
a = !a;      // useful if input predicates change
foo.print();

foo = 101;   // replace std::function input with assigned int
foo.print();

Is this an improvement over solely using a std::function<T()> for the input and using foo = []{return 42;} for fixed input values?

An alternative would be to make separate subclasses for assigned vs called inputs but that resulted in combinatorial explosion when there's more than one input. Are there other alternatives I'm missing?

Jack Deeth
  • 3,062
  • 3
  • 24
  • 39

1 Answers1

1

Mathematically speaking, the constant function is just another function. And in this C++ example, there seems to be no motivating reason to treat the constant function as a special case. Performance is likely to be approximately the same, unless the large majority of your inputs are constants.

Additionally, this functionable cannot be used with std::generate, while a std::function<> wrapping a constant can. That's fixable of course by wrapping functionable in a class of its own or capturing one in another lambda. But it's just adding complexity when the simple solution will do.

MSalters
  • 173,980
  • 10
  • 155
  • 350