Reputation: 1725
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
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)};
}
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
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
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
Reputation: 206597
You can do that by:
operator[]
defined.operator[]
in terms of the operator()
of a std::function
.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
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