Я знаю, что этот вопрос задавался раньше, но ни один из них не касался лямбда-выражений С++ 20.
Я просматривал новые функции С++ 20, и одна из них привлекла мое внимание — возможность использования лямбда-выражений в неопределенных контекстах.
Я видел, что вы можете написать код, подобный следующему, на видео Джейсона Тернера:
std::unique_ptr<int, decltype([] (FILE* f) {fclose(f);})>;
что предполагает, что std::unique_ptr
может принимать лямбда-параметр.
Поэтому я попытался реализовать ленивый математический класс последовательности, который выглядит следующим образом:
#include <vector>
#include <iostream>
template <typename func, typename T>
struct Sequence
{
T operator[] (int i) {
return func(i);
}
};
int main() {
auto u = std::vector{1 , 2, 3, 4};
auto v= Sequence<decltype([u] (int i) {return u[i];}) , int>();
std::cout << v[1];
return 0;
}
Идея состоит в том, что класс последовательности определяется как функция от N -> T (как в математике), и я передаю эту функцию во время компиляции в качестве параметра шаблона. Однако я получаю сообщение об ошибке «Нет соответствующего вызова для лямбда». Как это решить?
Я мог бы сохранить функцию в std::function во время выполнения, но я не могу позволить себе накладные расходы, которые она приносит.
@user202729 user202729 Могу ли я вызвать лямбду, зная только тип, если каждая лямбда имеет другой тип? Кроме того, если я не могу, как работает пример Джейсона Тернера с std::unique_ptr? Наверняка он вызывает лямбду?
Лямбда-выражения могут быть построены по умолчанию из их типа в c++20
. Но вы не можете по умолчанию построить захватывающую лямбду.
@super Я все еще получаю ту же ошибку, если передаю лямбду без захвата, т.е. [] (int i) {return i;}
Это потому, что вы пытаетесь вызвать тип вместо того, чтобы создавать экземпляр лямбды и вызывать его. Посмотрите здесь.
Как упоминалось в комментариях, проблема в том, что ваша лямбда захватывает u
. Следовательно, вы не можете создать экземпляр лямбда только из своего типа в C++20. Однако, если вы передаете лямбду напрямую как параметр, а не ее тип, и используете массив вместо вектора (конструктор vector's
и operator[]
не являются constexpr
), вы можете заставить его работать:
#include <array>
#include <iostream>
template <auto func>
struct Sequence
{
consteval auto operator[] (int i) const {
return func(i);
}
};
int main() {
constexpr auto u = std::array<int, 4>({1, 2, 3, 4});
constexpr auto l = [=](int i) { return u[i]; };
constexpr auto v = Sequence<l>{};
static_assert(v[1] == 2);
std::cout << v[1];
return 0;
}
Даже без включенной оптимизации operator[] никогда не вызывается во время выполнения.
Спасибо. Это отвечает на мой вопрос. Чего я действительно хотел, так это того, чтобы Sequence не оценивался во время компиляции, то есть чтобы он мог зависеть от типов времени выполнения, таких как std::vector. Но я думаю, что это невозможно...
На самом деле в C++20 vector
был изменен и может использоваться во время компиляции, но, похоже, он еще не принят gcc (constexpr auto u = std::vector<int>{1, 2, 3, 4};
терпит неудачу). Вы можете получить последовательность, созданную во время компиляции, которая operator[]
оценивается во время выполнения, но тогда ее состояние также должно быть конструируемым во время компиляции. Чтобы использовать его против вектора, вам нужно, например, передать вектор в качестве дополнительного параметра метода.
Подсказка:
func
— это тип лямбды, а не сама лямбда. Чтобы получить саму лямбду нужно ее где-то передать