Параметр шаблона, обозначающий «что-то вызываемое с определенной подписью»

Мне нужна была возможность писать такой код:

SplineFunction<Polynomial<3>> cubicSplineFunction;
// ... here be some additional code to populate the above object ...

auto dydx = cubicSplineFunction.transform<Polynomial<2>>(const Polynomial<3>& cubicSpline){
    return cubicSpline.derivative();
};

auto dsdx = cubicSplineFunction.transform<T/*?*/>([](const Polynomial<3>& cubicSpline){
    Polynomial<2> dy = cubicSpline.derivative();
    Polynomial<4> dsSquared = dy*dy + 1*1;
    return [dsSquared](double x){ // Fixed in response to comment: capture by value
        return std::sqrt(dsSquared);
    };
});

dydx(1.0); // efficient evaluation of cubicSplineFunction's derivative
dsdx(2.0); // efficient evaluation of cubicSplineFunction's arc rate

Итак, я реализовал классы ниже. Но каким типом я должен заменить T (в строке 8) выше, чтобы обозначить «что-то вызываемое с подписью double(double)»?

template<typename S>
struct SplineFunction {

    std::vector<S> splines;

    auto operator()(double t) const {
        int i = static_cast<int>(t);
        return splines[i](t - i);
    }

    template<typename R, typename F>
    SplineFunction <R> transform(F f) const {
        SplineFunction <R> tfs;
        for (const auto& s : splines) {
            tfs.splines.push_back(f(s));
        }
        return tfs;
    }

    // ... MORE CODE ...
}

template<int N>
struct Polynomial {
    std::array<double, N+1> coeffs;
    double operator()(double x) const;
    Polynomial<N - 1> derivative() const;

    // ... MORE CODE ...
}

template<int L, int M>
Polynomial<L+M> operator*(const Polynomial<L>& lhs, const Polynomial<M>& rhs);

template<int L>
Polynomial<L> operator+(Polynomial<L> lhs, double rhs);

// ... MORE CODE ...

Опасно, Уилл Робинсон! Второй вызов transform создает лямбду, которая захватывает локальную переменную по ссылке, а затем возвращает эту лямбду вызывающей стороне. Вызывающий получает лямбду, содержащую висящую ссылку.

Igor Tandetnik 02.05.2018 02:54

"что-то вызываемое с подписью double(double)" можно записать как std::function<double(double)>

Igor Tandetnik 02.05.2018 02:58

Другая возможность - полностью отбросить параметр R и определить тип возвращаемого значения: template<typename F> auto transform(F f) const -> SplineFunction<decltype(f(std::declval<S>()))> { ... }

Igor Tandetnik 02.05.2018 03:01

@IgorTandetnik Вы можете написать это с помощью std::function, но только с накладными расходами (как по производительности, так и по размеру). В данном случае это не имеет значения, но стоит отметить.

Daniel H 02.05.2018 04:30
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
80
1

Ответы 1

template<class F, class R=std::result_of_t<F&(S const&)>>
SplineFunction<R> transform(F f) const

не передавайте типы явно; пусть они будут выведены.

В делаем typename std::result_of<F&(S const&)>::type.

Распад типа R (как в std decay) также может быть разумным, поскольку SplineFunction хранит свой параметр шаблона, а decay делает типы более подходящими для хранения.

В C++ 17 std::result_of устарел.

Daniel H 02.05.2018 04:28

@daniel да; но причина в том, что его трудно использовать; Я правильно использовал. Версия C++ 2a / b - это механическое преобразование.

Yakk - Adam Nevraumont 02.05.2018 04:30

Я не говорю, что вы использовали его неправильно; Я выступаю против использования устаревших функций в целом (это затрудняет обновление) и особенно против того, чтобы делать это без указания того, что они устарели. Хотя в данном случае я думаю, что и result_of, и invoke_result - неподходящие инструменты для работы, и если бы я был на реальном компьютере, я бы расширил Предложение Игоря Тандетника по вычету возвращаемого типа до полного ответа.

Daniel H 02.05.2018 04:38

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