Как передать две лямбда-функции с помощью одного параметра шаблона в C++

Я пишу программу, которая численно вычисляет значение распределения Фойгта. Однако я сталкиваюсь с проблемой, когда пытаюсь передать функции Гаусса и Лоренца, используя один параметр шаблона класса F, даже если они одного типа.

Когда я использую два аргумента шаблона, скажем, F1 и F2, это работает как шарм. Но g++ выдает ошибку всякий раз, когда есть только одна. Немедленная передача лямбд в качестве параметров splot (свертки) не помогает.

#define _USE_MATH_DEFINES

#include <iostream>
#include <cmath>
#include <functional>

using namespace std;

#define MIN -10.
#define MAX 10.
#define EPS 0.01

template <typename T, class F> T trapezoid(F f, T a, T b, T eps) {
    T result = T(0);
    while (a <= b) {
        result += f(a);
        a += eps;
    }
    result -= (f(a) + f(b)) / T(2);
    return result * eps;
}

template <class F>
double splot(F g, F l, double x, double sigma, double gamma) {

    auto s = [g, l, x, sigma, gamma](double x_prime)->double {
        return g(x_prime, sigma) * l(x - x_prime, gamma);
    };

    return trapezoid(s, MIN, MAX, EPS);
}

int main (void) {
    double x = 0., sigma = 1.5, gamma = 0.1;

    auto gauss = [](double x, double sigma)->double {
        return exp(-1*x*x / (2*sigma*sigma)) / (sigma * sqrt(2*M_PI));
    };

    auto lorentz = [](double x, double gamma)->double {
        return gamma / (M_PI*(x*x + gamma*gamma));
    };

    cout << "x: " << x << endl << "V(x): " <<
     splot(gauss, lorentz, x, sigma, gamma) << endl;

    return 0;
}
Эта почта было бы полезно.
Hiroki 07.04.2019 16:46
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
78
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

В чем проблема ?

Если я хорошо понимаю ваш вопрос, когда вы определяете splot(), используя другой параметр шаблона для каждой переданной лямбды, он отлично компилируется:

template <class F1, class F2>
double splot(F1 g, F2 l, double x, double sigma, double gamma) {
    ...
}

Но обе лямбда-выражения, которые вы используете, имеют одинаковую сигнатуру (одинаковые типы аргументов и один и тот же тип возвращаемого значения), поэтому вы ожидаете, что они будут одного типа, и следующее определение splot() также будет скомпилировано:

template <class F>
double splot(F g, F l, double x, double sigma, double gamma) {
    ...
}

Но он не компилируется, и компилятор даже путает его с сообщение об ошибке, который предполагает, что обе лямбды имеют разные типы, тогда как отображаемое имя типа указывает на один и тот же тип:

note:   template argument deduction/substitution failed:
note:   deduced conflicting types for parameter ‘F’ (‘main()::<lambda(double, double)>’ and ‘main()::<lambda(double, double)>’) 

Почему возникла проблема?

Компилятор прав, несмотря на вводящее в заблуждение сообщение об ошибке. В выводе типа с помощью F есть ошибка: стандарт C++ утверждает в [expr.prim.lambda.closure]/1, что:

The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.

Таким образом, каждая лямбда имеет свой тип, даже если они имеют одинаковую сигнатуру.

Спасибо, я был введен в заблуждение, потому что наш учитель дал нам это в качестве домашнего задания, и он написал прототип функции только с одним классом шаблона.

guben 07.04.2019 18:20

Я бы посоветовал вам использовать std::function<>, как показано ниже:

typedef std::function<double(double,double)> func;

func gauss = [](double x, double sigma)->double {
    return exp(-1*x*x / (2*sigma*sigma)) / (sigma * sqrt(2*M_PI));
};

func lorentz = [](double x, double gamma)->double {
    return gamma / (M_PI*(x*x + gamma*gamma));
};

cout << "x: " << x << endl << "V(x): " <<
 splot(gauss, lorentz, x, sigma, gamma) << endl;`

как указал @Christophe, лямбда будет иметь разные типы, когда это, с другой стороны, гарантирует, что вы сохраните один и тот же тип для всех своих методов.

Хороший ! Я бы просто предложил более современную альтернативу typedef: using func = std::function<double(double,double)>;

Christophe 07.04.2019 17:03

Ага! Я всегда забываю об этой альтернативе, нужно привыкнуть к ней :)

grybouilli 07.04.2019 17:05

Это в основном потому, что они не одного типа.

#include <iostream>
#include <cmath>

using namespace std;

int main (void) {
  auto gauss = [](double x, double sigma)->double {
                 return exp(-1*x*x / (2*sigma*sigma)) / (sigma * sqrt(2*M_PI));
               };

  auto lorentz = [](double x, double gamma)->double {
                   return gamma / (M_PI*(x*x + gamma*gamma));
                 };

  cout << typeid(gauss).name() << endl;
  cout << typeid(lorentz).name() << endl;
  return 0;
}

Посмотрите, как он дает разные идентификаторы для каждой функции. Это едва объяснено в эта ссылка. Тогда, я думаю, вам придется придерживаться этого:

template <class F, class G>
double splot(F g, G l, double x, double sigma, double gamma) {

    auto s = [g, l, x, sigma, gamma](double x_prime)->double {
        return g(x_prime, sigma) * l(x - x_prime, gamma);
    };

    return trapezoid(s, MIN, MAX, EPS);
}

Или вы должны сделать шаблоны так же, как Compare или, может быть, использовать класс binary_function. См. пример.

Хорошая диагностика. Но также и некоторая удача, потому что нет никаких гарантий на name(), как объяснено здесь: другой компилятор может создать одно и то же name() для разных типов. Тогда ваша демонстрация будет столь же вводящей в заблуждение, как и сообщение об ошибке gcc.

Christophe 07.04.2019 17:15

Причина в том, что каждая лямбда имеет свой собственный тип, и ваша функция splot() требует, чтобы g и l были точно такого же типа. Если вы передадите ту же лямбду в splot(), вы заметите, что она скомпилируется нормально:

cout << "x: " << x << endl << "V(x): " <<
    splot(gauss, gauss, x, sigma, gamma) << endl;

Но нужны ли вообще шаблоны? Вам просто нужно убедиться, что первые два аргумента являются функциями требуемого типа, и вы уже знаете тип, поэтому вместо этого попробуйте следующее:

double splot(std::function<double(double, double)> g, std::function<double(double, double)> l, double x, double sigma, double gamma) {
    ...
}

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