Использование лямбды вместо индексного параметра шаблона - PullRequest
1 голос
/ 19 марта 2019

У меня есть метод, который принимает индексируемый объект в качестве параметра шаблона, что-то вроде:

template <typename OBJ>
int foo(int n, OBJ o)
{
  int x = 0;
  for (int i = 0; i < n; ++i) {
    x += o[i];
  }
  return x;
}

Есть ли способ передать лямбда-функцию для параметра o? Другими словами, иметь ли лямбду возможность вызова через оператор [] вместо оператора ()?

Ответы [ 4 ]

4 голосов
/ 19 марта 2019
template<class F>
struct square_bracket_invoke_t {
  F f;
  template<class T>
  auto operator[](T&& t)const
  -> typename std::result_of< F const&(T&&) >::type
  { return f(std::forward<T>(t)); }
};
template<class F>
square_bracket_invoke_t< typename std::decay<F>::type >
make_square_bracket_invoke( F&& f ) {
  return {std::forward<F>(f)};
}

Живой пример .

Код - C ++ 11 и имеет в основном нулевые накладные расходы.

int main() {
  std::cout << foo( 6, make_square_bracket_invoke([](int x){ return x; } ) ) << "\n";
}

результат равен 0 + 1 + 2 +3 + 4 + 5 ака 15.

Это хорошая идея?Может быть.Но зачем останавливаться на достигнутом?

Для максимального удовольствия:

const auto idx_is = make_square_bracket_invoke([](auto&&f){return make_square_bracket_invoke(decltype(f)(f));});
int main() {
  std::cout << foo( 6, idx_is[[](int x){ return x; }] ) << "\n";
}
2 голосов
/ 19 марта 2019

Вы можете сделать это:

  1. Создание шаблона класса, функтора, для которого определен operator[].
  2. Реализация operator[] в терминах operator() из std::function.
  3. Хранение лямбды в оболочке std::function в качестве переменной-члена шаблона класса.

Вот демонстрационная программа.

#include <iostream>
#include <functional>

template <typename OBJ>
int foo(int n, OBJ o)
{
  int x = 0;
  for (int i = 0; i < n; ++i) {
    x += o[i];
  }
  return x;
}

template <typename> struct Functor;

template <typename R> struct Functor<R(int)>
{
   using ftype = std::function<R(int)>;
   Functor(ftype f) : f_(f) {}

   R operator[](int i) const { return f_(i); }

   ftype f_;
};

int main()
{
   Functor<int(int)> f = {[](int i) -> int {return i*i;}};
   std::cout << foo(10, f) << std::endl;
}

и его выход

285

Демонстрационная версия

PS

Functor здесь не подходит. Он не перегружает оператор вызова функции. Я подозреваю, что есть более подходящее имя.

1 голос
/ 19 марта 2019

Ну, если это поможет, вот способ переадресовать класс-оболочку operator[] на вашу лямбду operator().

template<class F>
struct SubscriptWrapper_t {
  F f_;
  template<class T> auto operator[](T const& t_) const -> decltype(f_(t_)) { 
    return f_(t_); 
  }
};
template<class F> 
SubscriptWrapper_t<typename std::decay<F>::type> SubscriptWrapper(F&& f_) {
  return{std::forward<F>(f_)}; 
}

Я часто использую такие обертки. Они удобны, и у них, похоже, нет вычислительных затрат, по крайней мере, при компиляции GCC. Вы можете сделать один за at или даже сделать за find.

РЕДАКТИРОВАТЬ: Обновлено для C ++ 11 (и обновлено, чтобы иметь возможность возвращать ссылку)

0 голосов
/ 19 марта 2019

Эскиз типа оболочки, который будет делать это.

template<typename UnaryFunction>
class index_wrapper
{
public:
    index_wrapper(UnaryFunction func) : func(std::move(func)) {}

    template<typename T>
    std::invoke_result_t<UnaryFunction, T> operator[](T&& t)
    { return func(std::forward<T>(t)); }

private:
    UnaryFunction func;
};

С использованием

#include <iostream>

template <typename OBJ>
int foo(int n, OBJ o)
{
  int x = 0;
  for (int i = 0; i < n; ++i) {
    x += o[i];
  }
  return x;
}

int main()
{
  index_wrapper f([](int i) -> int { return i*i; });
  std::cout << foo(10, f) << std::endl;
}

Возможно, вы захотите ограничить его одним типом параметра, чтобы вы могли предоставить псевдонимы типа элемента, подобные std::vector::reference et.al.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...