2013-02-28 44 views
5

Trong MATLAB, người ta có thể viết:Làm thế nào để "bỏ" một hàm hai đối số thành một hàm đối số?

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

Nếu tôi có một chức năng hy vọng một chức năng một lập luận, tôi có thể làm các việc trên. Làm thế nào tôi có thể làm điều này trong c/c + +?

Tôi có chức năng thư viện (từ thư viện CGAL) dự kiến ​​là đối số một hàm mà chính nó chỉ có một đối số. Lý tưởng nhất, tôi có một lớp (SphericalHarmonics) và tôi muốn có một chức năng thành viên mà có một đối số. Vì vậy, tôi có:

FT SphericalHarmonics::distFunction(Point_3 p) 

(lưu ý rằng FT là một loại tương tự như double) nhưng tất nhiên khi tôi cố gắng

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

this cũng được coi là một cuộc tranh cãi, chức năng distFunction của tôi là một hai chức năng đối số và lỗi được ném.

Lưu ý rằng điều này có thể được giải quyết với các biến toàn cục, ví dụ:

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

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

Tuy nhiên, điều này thực sự không phải là lý tưởng. Tôi muốn một cách để làm điều này mà không có biến toàn cầu, vì nó sẽ là tốt hơn để có thể có một hàm lớp dễ dàng tích hợp với thư viện CGAL.

Cảm ơn trước!

[CẬP NHẬT]

@ Andy-Prowl: Tôi đã thử std::bindlambda bạn giải pháp, nhưng vẫn dường như đang chạy vào các lỗi liên quan đến số lượng các đối số.

Khi nào, trong main, tôi sử dụng mã:

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.)); 

tôi nhận được lỗi:

~/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 

[CẬP NHẬT]

Nó bây giờ đã rõ ràng với tôi rằng tôi cần một chức năng mà có thể được chuyển đổi sang một con trỏ hàm (ví dụ: surface yêu cầu đối số con trỏ hàm). Quy tắc này đưa ra tùy chọn std::bind. Hơn nữa, có vẻ như một lambda không thể được chuyển đổi thành một con trỏ hàm nếu nó nắm bắt các biến (capture-less vs. capturing lambdas). Vì vậy, tôi nghĩ câu trả lời của Andy-Prowl dưới đây nói chung là câu trả lời đúng cho câu hỏi này, mặc dù tôi sẽ cần phải tìm một công việc khác.

+3

(Tôi đã không thực sự đọc câu hỏi) ràng buộc hoặc một lambda? –

+0

Các thẻ gây hiểu lầm, nên chứa matlab/lambda – Dmitry

+0

Có bất kỳ giải pháp nào trong số này thực sự hoạt động không? Không thể sử dụng liên kết để đứng trong con trỏ hàm? –

Trả lời

5

OPTION 1:

Trong trường hợp chức năng thành viên của bạn không để ngầm làm việc trên một thể hiện của lớp học của bạn (và do đó không cần phải nhận được một this con trỏ), bạn có thể làm cho nó static:

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

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

Bây giờ, chức năng của bạn sẽ có hiệu quả có một cuộc tranh luận duy nhất:

surface(SphericalHarmonics::distFunction); 

OPTION 2:

Nếu không, bạn có thể sử dụng std::bind() để cà ri có các hàm thành viên distFunction và sửa chữa đầu tiên, lập luận ngầm của nó (nếu bạn không được làm việc với một trình biên dịch C++ 11, bạn có thể sử dụng tương đương boost::bind() từ thư viện Boost.Bind):

#include <functional> 

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

OPTION 3:

Ngoài ra, trong C++ 11, một lambda có thể thực hiện công việc:

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

Cảm ơn! Tôi chắc chắn điều này là chính xác vì vậy tôi đã kiểm tra đánh dấu nó. Tùy chọn 1 không phải là một tùy chọn (tôi muốn làm việc trên các cá thể lớp cụ thể). Khi tôi thử một trong hai lựa chọn 2 hoặc 3 tôi nhận được lỗi 'lỗi: 'fxn' không đặt tên một loại' và' lỗi: 'fxn' không được khai báo trong phạm vi này. Tôi đã thử cả hai 'std :: bind' và' boost :: bind' (includind '' và '') và tôi gặp lỗi tương tự. Sử dụng gcc phiên bản 4.6.3 trên ubuntu 12.04 – OwenM

+0

@OwenM: Bạn đang sử dụng trình biên dịch C++ 11 với tùy chọn dòng lệnh '-std = C++ 0x' hoặc' -std = C++ 11'? Từ khóa 'auto' yêu cầu C++ 11. Tôi cũng đề nghị bạn nâng cấp lên GCC 4.7.2, phiên bản bạn đang sử dụng khá cũ. Ngoài ra còn có [trang web này] (http://www.liveworkspace.org), nơi bạn có thể biên dịch mã của mình bằng các trình biên dịch khác nhau. –

+0

Yep, gcc 4.7.2 với -std = C++ 11 khắc phục sự cố này! – OwenM

5

Sử dụng std::bind hoặc boost::bind:

#include <functional> 

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

Bạn có nghĩa là sh thay vì điều này? – Slava

+0

@Slava Yep, cảm ơn. – Drax

1

Trong trường hợp cụ thể để đối phó với lớp templated Surface_3 CGAL của. có lẽ bạn đang sử dụng một cái gì đó như thế này (từ tấm gương của họ) để xác định Surface_3 loại:

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; 

Đây là gây hiểu lầm vì nó làm cho nó có vẻ như các lớp học isosurface CGAL của chỉ có thể đối phó với con trỏ hàm (như trái ngược với std::function v.v.) Nhưng vấn đề chỉ là chúng tôi đã chỉ xác định nó theo cách đó. Đơn giản chỉ cần xác định Surface_3 sử dụng std::function<FT (Point_3)> như là đối số mẫu của nó và std::bind và lambdas từ Andy Prowl's answer sẽ chỉ làm việc tốt:

... 
typedef std::function<FT (Point_3)> Function; 
typedef CGAL::Implicit_surface_3<GT, Function> Surface_3; 
Các vấn đề liên quan