Как передать функцию в качестве параметра шаблона в контейнер, который не хранит элементы, а вычисляет их на лету?
Цель
Нужен контейнер, который будет обладать следующими свойствами:
Не хранит ни один из элементов, вместо этого он вычисляет значение каждого элемента 'на лету', по мере надобности
Как почти в любом контейнере есть begin() и end().
Хранит только количество элементов. То есть сами элементы он не хранит, он должен вычислять их на лету.
template <class Func, size_t N> struct PseudoContainer{ size_t size = N; ... operator[](size_t n){ return Func(n); } ... begin(){ } ... end(){ } };
Задумка у этого контейнера такая:
Cоздаём контейнер, который будет 'хранить' пять экземпляров некоторого класса
struct some_struct {
some_struct(size_t n) : pow(n*n){
}
size_t pow;
};
PseudeContainer</*Сюда нужна как-то передать функцию*/, 5> c;
Теперь должна быть возможность получать значения
auto a = c.begin(); // эквивалентно some_struct(0);
auto b = c[0]; // эквивалентно some_struct(0);
auto c = c[1]; // эквивалентно some_struct(1);
for(auto el: c){
// что-то делаем с элементом
}
В качестве функции может передаваться как класс (как в примере выше), так и функция, например
int foo(int n){
return n*n;
}
PseudeContainer<foo, 5> c;
auto a = c.begin(); // эквивалентно some_struct(0);
auto b = c[0]; // эквивалентно some_struct(0);
auto c = c[1]; // эквивалентно some_struct(1);
Собственно вопросы
- Как передать в параметр шаблона функцию?
- Как определить тип возвращаемого значения?
- Как правильно реализовать begin() и end()? Нужно написать итератор, который будет хранить значение n внутри себя?
- Может есть что-то подобное в boost или других библиотеках?
Моя попытка выглядит так
struct some_struct {
some_struct(size_t n) : pow(n*n){
}
size_t pow;
};
template <class Func, size_t Size>
struct PseudoContainer {
using value_type = decltype(STD declval<Func(0)>()); // Тут неправильно
using reference = value_type&;
reference operator[](size_type pos) noexcept {
return Func(pos);
}
size_t size = Size;
};
int main(){
PseudeContainer<some_struct, 5> c; // ничего не работает
auto _0 = c.begin();
auto _1 = c[1];
for(auto el: c){
auto _n = el;
}
}
Ответы (1 шт):
Я вот набросал такой код. Он может чуть странно выглядит, но как рабочий пример подойдет.
#include <iostream>
#include <stdexcept>
struct foo
{
int operator()(int n) { return n*n;}
};
template <typename D, size_t S>
class PseudeContainer
{
static D m_f;
public:
// если мы хотим begin/end, то хорошо бы итератор сделать
class my_iterator
{
size_t m_pos;
public:
explicit my_iterator(size_t position = 0) : m_pos{position} {}
// при разыменовании - вызываем функцию от текущей позиции
int operator*() { return m_f(m_pos);}
// при переходе вперед, просто увеличиваем содержимое итератора
my_iterator& operator++() {
++m_pos;
return *this;
}
// итератор должен знать, когда останавливаться
bool operator!=(const my_iterator& other) {
return m_pos != other.m_pos;
}
};
const auto operator[](size_t index) {
if (index > S) throw std::out_of_range("bad index");
return m_f(index);
}
my_iterator begin() { return my_iterator(0);}
my_iterator end() { return my_iterator(S-1);}
};
int main()
{
PseudeContainer<foo, 5> c;
auto a = c.begin(); // эквивалентно some_struct(0);
auto b = c[0]; // эквивалентно some_struct(0);
auto z = c[1]; // эквивалентно some_struct(1);
std::cout << z << '\n';
for (auto a : c) {
std::cout << a << '\n';
}
}
Теперь ответы
Как передать в параметр шаблона функцию?
если посмотреть на stl, то каждый раз, когда нужно передать функцию как параметр шаблона, там передается функтор. Собственно я это и сделал.
Как определить тип возвращаемого значения?
Можно посмотреть, как это делают люди https://stackoverflow.com/questions/41301536/get-function-return-type-in-template или посмотреть на встроенный result_of / invoke_of, но в Notes какой то треш, первая функция была задеприкейчена...
Но есть обходной путь, который просто все решает - auto. Если придираться к стандарту, то это с 14 можно только делать, но большинство компиляторов умеет и с 11. И мне этот способ очень нравится. Компилятор сам быстро и красиво разберется с типом, чем разворачивать десятки хитрых шаблонов.
Как правильно реализовать begin() и end()? Нужно написать итератор, который будет хранить значение n внутри себя?
да. где то так. см. пример выше. Я там реализовал самый минимум, но по обстоятельствам можно добавить своих нужных. В моем случае итератор хранит номер позиции, а когда разыменовывают, то он вызывает сохраненную функцию.
Может есть что-то подобное в boost или других библиотеках?
Возможно. Буст он такой. Но из за того, что он такой большой, часто легче написать свое, чем тащить его за собой.