Как "преобразовать" функцию с двумя аргументами в функцию с одним аргументом?

В Matlab можно написать:

S = @(x,y) x^2+y^2-1
G = @(x) S(x,1);

Если у меня есть функция, ожидающая функцию с одним аргументом, я могу сделать выше. Как я могу сделать это в C / C++?

У меня есть библиотечная функция (из библиотеки CGAL), которая ожидает в качестве аргумента функцию, которая сама имеет только один аргумент. В идеале у меня есть класс (SphericalHarmonics) и я хотел бы иметь функцию-член, которая принимает один аргумент. Так что я:

FT SphericalHarmonics::distFunction(Point_3 p)

(Обратите внимание, что FT тип похож на double) но конечно когда попробую

SphericalHarmonics *sh = new SphericalHarmonics(1);
Surface_3 surface(sh->distFunction, Sphere(ORIGIN,2.));

this также рассматривается как аргумент, мой distFunction Функция является функцией с двумя аргументами, и выдается ошибка.

Обратите внимание, что это можно решить с помощью глобальных переменных, т.е.

SphericalHarmonics *sh;
FT dist_function(Point_3 p) {
    return sh->distFunction(p);
}

main() {
    sh = new SphericalHarmonics(1);
    Surface_3 surface(dist_function);
}

Тем не менее, это действительно не идеально. Я хотел бы сделать это без глобальных переменных, так как было бы гораздо лучше иметь функцию класса, которая легко интегрируется с библиотекой CGAL.

Заранее спасибо!

[ОБНОВЛЕНО]

@ Энди-Prowl: я попробовал ваш std::bind а также lambda решения, но все же, кажется, сталкиваются с ошибками в отношении количества аргументов.

Когда в mainЯ использую код:

SphericalHarmonics *sh = new SphericalHarmonics(cInit, numL, symm);
auto fxn = std::bind(&SphericalHarmonics::distFunction, sh, std::placeholders::_1);
Surface_3 surface(fxn, Sphere_3(ORIGIN,2.));

Я получаю ошибки:

~/lib/basisfunctions/SphericalHarmonics2/mesh_an_implicit_function.cpp:62:48: 
error: no matching function for call to     
‘CGAL::Implicit_surface_3<CGAL::Robust_circumcenter_traits_3<CGAL::Epick>, 
double (*)
(CGAL::Point_3<CGAL::Epick>)>::Implicit_surface_3(std::_Bind<std::_Mem_fn
<double (SphericalHarmonics::*)(CGAL::Point_3<CGAL::Epick>)>
(SphericalHarmonics*, std::_Placeholder<1>)>&, Sphere_3)’

а также

~/CGAL-4.1/include/CGAL/Implicit_surface_3.h:50:5: note:   no known conversion 
for argument 1 from ‘std::_Bind<std::_Mem_fn<double (SphericalHarmonics::*)
(CGAL::Point_3<CGAL::Epick>)>(SphericalHarmonics*, std::_Placeholder<1>)>’ to 
‘CGAL::Implicit_surface_3<CGAL::Robust_circumcenter_traits_3<CGAL::Epick>, 
double (*)(CGAL::Point_3<CGAL::Epick>)>::Function 
{aka double (*)(CGAL::Point_3<CGAL::Epick>)}’

а также

~/CGAL-4.1/include/CGAL/Implicit_surface_3.h:34:9: note:   
candidate expects 1 argument, 2 provided

[ОБНОВЛЕНО]

Теперь мне ясно, что мне нужна функция, которую можно преобразовать в указатель функции (т.е. surface требуется аргумент указателя на функцию). Это исключает std::bind вариант. Более того, кажется, что лямбда не может быть преобразована в указатель на функцию, если она захватывает переменные (без захвата или захвата лямбда-выражения). Таким образом, я думаю, что ответ Энди-Проул ниже является в целом правильным ответом на этот вопрос, хотя мне нужно будет найти другой обходной путь.

3 ответа

Решение

ОПЦИЯ 1:

В случае, если ваша функция-член не может неявно работать с экземпляром вашего класса (и, следовательно, не нужно получать this указатель), вы можете сделать это static:

class SphericalHarmonics
{
    ...
    static double distFunction(Point p);
    ...
};

double SphericalHarmonics::distFunction(Point p)
{
    ...
}

Теперь ваша функция будет иметь единственный аргумент:

surface(SphericalHarmonics::distFunction);

ВАРИАНТ 2:

В противном случае вы можете использовать std::bind() карри функцию-член distFunction и исправьте его первый неявный аргумент (если вы не работаете с компилятором C++11, вы можете использовать эквивалентный boost::bind() из библиотеки Boost.Bind):

#include <functional>

SphericalHarmonics *sh = new SphericalHarmonics(1);
auto fxn = std::bind(&SphericalHarmonics::distFunction, sh, _1);
surface(fxn);

ВАРИАНТ 3:

В качестве альтернативы, в C++11 лямбда может сделать эту работу:

SphericalHarmonics *sh = new SphericalHarmonics(1);
auto fxn = [=] (double d) { return sh->distFunction(d); } 

Используйте std::bind или boost:: bind:

#include <functional>   

SphericalHarmonics *sh = new SphericalHarmonics(1);
surface(std::bind(&SphericalHarmonics::distFunction, sh, _1));

В конкретном случае борьбы с шаблонами CGAL Surface_3 учебный класс. Вы, вероятно, используете что-то вроде этого (из их примера) для определения Surface_3 тип:

typedef CGAL::Surface_mesh_default_triangulation_3 Tr;
// c2t3
typedef CGAL::Complex_2_in_triangulation_3<Tr> C2t3;
typedef Tr::Geom_traits GT;
typedef GT::Sphere_3 Sphere_3;
typedef GT::Point_3 Point_3;
typedef GT::FT FT;
typedef FT (*Function)(Point_3);
typedef CGAL::Implicit_surface_3<GT, Function> Surface_3;

Это вводит в заблуждение, потому что кажется, что классы изоповерхностей CGAL могут иметь дело только с указателями на функции (в отличие от std::function так далее.). Но проблема только в том, что мы только что определили это таким образом. Просто определить Surface_3 использовать std::function<FT (Point_3)> в качестве аргумента шаблона и тому std::bind и лямбда из ответа Энди Провла будет работать просто отлично:

...
typedef std::function<FT (Point_3)> Function;
typedef CGAL::Implicit_surface_3<GT, Function> Surface_3;
Другие вопросы по тегам