Tôi thường xuyên sử dụng boost.lambda (và phoenix) để định nghĩa hàm lambda trong C++. Tôi thực sự thích tài sản đa hình của họ, sự đơn giản của sự trình bày của họ và cách họ tạo lập trình hàm trong C++ dễ dàng hơn rất nhiều. Trong một số trường hợp, nó thậm chí còn sạch hơn và dễ đọc hơn (nếu bạn đang sử dụng để đọc chúng) để sử dụng chúng để xác định các chức năng nhỏ và đặt tên chúng trong phạm vi tĩnh.Các hàm tĩnh từ boost.lambda hoặc boost.phoenix
Cách để lưu trữ những functionals tương tự như chức năng thông thường nhất là để nắm bắt chúng trong một boost::function
const boost::function<double(double,double)> add = _1+_2;
Nhưng vấn đề là không hiệu quả thời gian chạy của làm như vậy. Mặc dù hàm add
ở đây là không quốc tịch, kiểu lambda trả lại không trống và sizeof
lớn hơn 1 (vì vậy boost::function
ctor mặc định và ctor sao chép sẽ liên quan đến new
). Tôi thực sự nghi ngờ rằng có một cơ chế từ trình biên dịch hoặc một bên của động lực để phát hiện statelessness này và tạo ra mã tương đương với sử dụng:
double (* const add)(double,double) = _1+_2; //not valid right now
Người ta có thể sử dụng tất nhiên C++ 11 auto
, nhưng sau đó biến không thể được thông qua xung quanh các bối cảnh không có khuôn mẫu. Cuối cùng tôi đã quản lý để làm hầu hết những gì tôi muốn, bằng cách sử dụng phương pháp sau đây:
#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;
#include <boost/type_traits.hpp>
#include <boost/utility/result_of.hpp>
using namespace boost;
template <class T>
struct static_lambda {
static const T* const t;
// Define a static function that calls the functional t
template <class arg1type, class arg2type>
static typename result_of<T(arg1type,arg2type)>::type
apply(arg1type arg1,arg2type arg2){
return (*t)(arg1,arg2);
}
// The conversion operator
template<class func_type>
operator func_type*() {
typedef typename function_traits<func_type>::arg1_type arg1type;
typedef typename function_traits<func_type>::arg2_type arg2type;
return &static_lambda<T>::apply<arg1type,arg2type>;
}
};
template <class T>
const T* const static_lambda<T>::t = 0;
template <class T>
static_lambda<T> make_static(T t) {return static_lambda<T>();}
#include <iostream>
#include <cstdio>
int main() {
int c=5;
int (*add) (int,int) = make_static(_1+_2);
// We can even define arrays with the following syntax
double (*const func_array[])(double,double) = {make_static(_1+_2),make_static(_1*_2*ref(c))};
std::cout<<func_array[0](10,15)<<"\n";
std::fflush(stdout);
std::cout<<func_array[1](10,15); // should cause segmentation fault since func_array[1] has state
}
Biên soạn với gcc 4.6.1 Kết quả của chương trình này là (không phụ thuộc vào mức độ tối ưu hóa):
25
Segmentation fault
như mong đợi. Ở đây, tôi giữ một con trỏ tĩnh đến kiểu biểu thức lambda (như const càng tốt cho mục đích tối ưu hóa) và khởi tạo nó thành NULL
. Bằng cách này, nếu bạn cố gắng "tĩnh" một biểu thức lambda với trạng thái, bạn chắc chắn sẽ nhận được một lỗi thời gian chạy. Và nếu bạn tĩnh một biểu thức lambda thực sự không trạng thái, mọi thứ sẽ hoạt động.
On cho câu hỏi (s):
Phương pháp này có vẻ hơi bẩn, bạn có thể nghĩ ra bất kỳ hoàn cảnh nào, hoặc biên dịch giả định rằng sẽ làm cho misbehave này (hành vi mong đợi: hoạt động tốt nếu lambda là không quốc tịch, segfault khác).
Bạn có thể nghĩ ra bất kỳ cách nào để thực hiện điều này sẽ gây ra lỗi trình biên dịch thay vì segfault khi biểu thức lambda có trạng thái không?
EDIT sau khi câu trả lời Eric Niebler của:
#include <boost/phoenix.hpp>
using namespace boost::phoenix;
using namespace boost::phoenix::arg_names;
#include <boost/type_traits.hpp>
#include <boost/utility/result_of.hpp>
using boost::function_traits;
template <class T>
struct static_lambda {
static const T t;
// A static function that simply applies t
template <class arg1type, class arg2type>
static typename boost::result_of<T(arg1type,arg2type)>::type
apply(arg1type arg1,arg2type arg2){
return t(arg1,arg2);
}
// Conversion to a function pointer
template<class func_type>
operator func_type*() {
typedef typename function_traits<func_type>::arg1_type arg1type;
typedef typename function_traits<func_type>::arg2_type arg2type;
return &static_lambda<T>::apply<arg1type,arg2type>;
}
};
template <class T>
const T static_lambda<T>::t; // Default initialize the functional
template <class T>
static_lambda<T> make_static(T t) {return static_lambda<T>();}
#include <iostream>
#include <cstdio>
int main() {
int (*add) (int,int) = make_static(_1+_2);
std::cout<<add(10,15)<<"\n";
int c=5;
// int (*add_with_ref) (int,int) = make_static(_1+_2+ref(c)); causes compiler error as desired
}
IIRC, nếu bạn đang sử dụng Phoenix, bạn có thể lưu trữ kết quả bên trong hàm 'boost :: phoenix ::' chứ không phải hàm' boost ::' và giảm thiểu một số tổn thất hiệu quả ('boost :: phoenix: : function' là các loại POD và có thể được khởi tạo tĩnh tại thời gian biên dịch). – ildjarn
@ildjarn Cảm ơn những người đứng đầu về 'boost :: phoenix :: function', đó là ràng buộc hữu ích trong nhiều trường hợp. Tôi vẫn quan tâm đến việc có được hàm lambda tương đương với native (hiệu năng thời gian chạy hiệu năng). Tôi không chắc chắn nếu nó có thể làm cho chất lượng sản xuất này, nhưng tôi tìm thấy sự theo đuổi thú vị. – enobayram