Передача лямбда в качестве параметра шаблона? ( С++ 20, лямбда-выражения в невычисленном контексте)

Я знаю, что этот вопрос задавался раньше, но ни один из них не касался лямбда-выражений С++ 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 во время выполнения, но я не могу позволить себе накладные расходы, которые она приносит.

Подсказка: func — это тип лямбды, а не сама лямбда. Чтобы получить саму лямбду нужно ее где-то передать

user202729 21.12.2020 10:43

@user202729 user202729 Могу ли я вызвать лямбду, зная только тип, если каждая лямбда имеет другой тип? Кроме того, если я не могу, как работает пример Джейсона Тернера с std::unique_ptr? Наверняка он вызывает лямбду?

SomeProgrammer 21.12.2020 10:45

Лямбда-выражения могут быть построены по умолчанию из их типа в c++20. Но вы не можете по умолчанию построить захватывающую лямбду.

super 21.12.2020 10:48

@super Я все еще получаю ту же ошибку, если передаю лямбду без захвата, т.е. [] (int i) {return i;}

SomeProgrammer 21.12.2020 10:50

Это потому, что вы пытаетесь вызвать тип вместо того, чтобы создавать экземпляр лямбды и вызывать его. Посмотрите здесь.

super 21.12.2020 10:51
Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
1
5
446
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как упоминалось в комментариях, проблема в том, что ваша лямбда захватывает 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. Но я думаю, что это невозможно...

SomeProgrammer 21.12.2020 13:48

На самом деле в C++20 vector был изменен и может использоваться во время компиляции, но, похоже, он еще не принят gcc (constexpr auto u = std::vector<int>{1, 2, 3, 4}; терпит неудачу). Вы можете получить последовательность, созданную во время компиляции, которая operator[] оценивается во время выполнения, но тогда ее состояние также должно быть конструируемым во время компиляции. Чтобы использовать его против вектора, вам нужно, например, передать вектор в качестве дополнительного параметра метода.

Artefacto 21.12.2020 14:49

Другие вопросы по теме