dlasalle
dlasalle

Reputation: 1725

Using a lambda in place of an index-able template parameter

I have a method that takes an index-able object as a template parameter, something like:

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

Is there a way I can pass a lambda function in for the o parameter? In other words, having the lambda be call-able via the [] operator rather than the () operator?

Upvotes: 1

Views: 102

Answers (4)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275395

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)};
}

Live example.

Code is C++11 and has basically zero overhead.

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

result is 0+1+2+3+4+5 aka 15.

Is this a good idea? Maybe. But why stop there?

For max amusement:

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";
}

Upvotes: 4

hegel5000
hegel5000

Reputation: 884

Well, if it helps, here's a way to forward a wrapper class's operator[] to your lambda's 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_)}; 
}

I use wrappers like this a lot. They're convenient, and they don't seem to have any computational overhead, at least when compiled by GCC. You can make one for at or even make one for find.

EDIT: Updated for C++11 (and updated to be able to return a reference)

Upvotes: 1

Caleth
Caleth

Reputation: 62704

A sketch of a wrapper type that would do this.

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;
};

With usage

#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;
}

You might want to restrict it to a single parameter type, so that you can provide member type aliases similar to std::vector::reference et.al.

Upvotes: 0

R Sahu
R Sahu

Reputation: 206597

You can do that by:

  1. Creating a class template, a functor, that has the operator[] defined.
  2. Implementing the operator[] in terms of the operator() of a std::function.
  3. Storing the lambda in a wrapped std::function as a member variable of the class template.

Here's a demonstrative program.

#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;
}

and its output

285

Live demo

PS

Functor is not the appropriate name here. It does not overload the function call operator. I suspect there is a more appropriate name.

Upvotes: 2

Related Questions