#c 11 #templates #lambda
#c 11 #шаблоны #лямбда
Вопрос:
У меня есть метод, который принимает индексируемый объект в качестве параметра шаблона, что-то вроде:
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
параметра? Другими словами, возможность вызова лямбда-выражения через []
оператор, а не ()
operator?
Комментарии:
1. Нет, это невозможно. Вам нужно передать объект, который реализует
operator[]
, а лямбда-объекты этого не делают.
Ответ №1:
template<class F>
struct square_bracket_invoke_t {
F f;
template<class T>
auto operator[](Tamp;amp; t)const
-> typename std::result_of< F constamp;(Tamp;amp;) >::type
{ return f(std::forward<T>(t)); }
};
template<class F>
square_bracket_invoke_t< typename std::decay<F>::type >
make_square_bracket_invoke( Famp;amp; 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([](autoamp;amp;f){return make_square_bracket_invoke(decltype(f)(f));});
int main() {
std::cout << foo( 6, idx_is[[](int x){ return x; }] ) << "n";
}
Комментарии:
1. Очень приятно. Вопрос для моего назидания. Что
std::decay<F>::type
делает для лямбда-выражения?2. @RSahu Если
F
естьX constamp;
, то оноX
есть. Если это так, тоXamp;
этоX
так. Если это так, тоX
этоX
так. За исключением, еслиF
это функция или ссылка на функцию или массив или ссылка на тип массива. Лямбды не являются ни одним из них.decay
делает тип подходящим для хранения в качестве значения; например, передавая его в функцию.3. Спасибо за разъяснение. Есть еще много аспектов C 11, которых мне еще предстоит достичь.
Ответ №2:
Вы можете сделать это с помощью:
- Создание шаблона класса, функтора, который имеет
operator[]
определенный. - Реализация
operator[]
в терминахoperator()
std::function
a-ов. - Сохранение лямбда-выражения в обернутом виде
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. Мне не нравится передавать подпись.
Ответ №3:
Что ж, если это поможет, вот способ переслать класс-оболочку operator[]
в ваш лямбда-класс operator()
.
template<class F>
struct SubscriptWrapper_t {
F f_;
template<class T> auto operator[](T constamp; t_) const -> decltype(f_(t_)) {
return f_(t_);
}
};
template<class F>
SubscriptWrapper_t<typename std::decay<F>::type> SubscriptWrapper(Famp;amp; f_) {
return{std::forward<F>(f_)};
}
Я часто использую подобные обертки. Они удобны и, похоже, не требуют каких-либо вычислительных затрат, по крайней мере, при компиляции с помощью GCC. Вы можете создать его для at
или даже создать для find
.
РЕДАКТИРОВАТЬ: Обновлено для C 11 (и обновлено, чтобы иметь возможность возвращать ссылку)
Комментарии:
1. не
decltype(auto)
в C 14? В 11, который запросил OP, это не сработает.2. @Yakk-AdamNevraumont Мне следовало лучше прочитать теги вопросов. Сейчас он обновлен, хотя и сделан избыточным.
Ответ №4:
Набросок типа-оболочки, который мог бы это сделать.
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[](Tamp;amp; 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
и др.
Комментарии:
1. Это C 17 — OP запросил 11