Trong C ++ 11, có cách nào để tạo mẫu hàm lambda không? Hoặc nó vốn đã quá cụ thể để được templated?
Tôi hiểu rằng tôi có thể định nghĩa một lớp / functor cổ điển thay thế, nhưng câu hỏi giống như: ngôn ngữ có cho phép tạo các hàm lambda không?
Trong C ++ 11, có cách nào để tạo mẫu hàm lambda không? Hoặc nó vốn đã quá cụ thể để được templated?
Tôi hiểu rằng tôi có thể định nghĩa một lớp / functor cổ điển thay thế, nhưng câu hỏi giống như: ngôn ngữ có cho phép tạo các hàm lambda không?
Câu trả lời:
CẬP NHẬT 2018: C ++ 20 sẽ đi kèm với lambdas templated và khái niệm hóa. Các tính năng đã được tích hợp vào dự thảo tiêu chuẩn.
CẬP NHẬT 2014: C ++ 14 đã được phát hành trong năm nay và hiện cung cấp lambdas đa hình với cú pháp giống như trong ví dụ này. Một số trình biên dịch chính đã thực hiện nó.
Tại nó đứng (trong C ++ 11), đáng buồn là không. Lambdas đa hình sẽ là tuyệt vời về sự linh hoạt và sức mạnh.
Lý do ban đầu họ cuối cùng là đơn hình là vì các khái niệm. Các khái niệm làm cho tình huống mã này trở nên khó khăn:
template <Constraint T>
void foo(T x)
{
auto bar = [](auto x){}; // imaginary syntax
}
Trong một mẫu bị ràng buộc, bạn chỉ có thể gọi các mẫu bị ràng buộc khác. (Nếu không, các ràng buộc không thể được kiểm tra.) Có thể foo
gọi bar(x)
? Lambda có những ràng buộc nào (tham số cho nó chỉ là một mẫu)?
Các khái niệm chưa sẵn sàng để giải quyết vấn đề này; nó sẽ yêu cầu nhiều thứ hơn như late_check
(trong đó khái niệm không được kiểm tra cho đến khi được gọi) và nội dung. Đơn giản hơn chỉ là thả tất cả và dính vào lambdas đơn hình.
Tuy nhiên, với việc loại bỏ các khái niệm khỏi C ++ 0x, lambdas đa hình lại trở thành một đề xuất đơn giản. Tuy nhiên, tôi không thể tìm thấy bất kỳ đề xuất nào cho nó. :
C ++ 11 lambdas không thể được tạo khuôn mẫu như đã nêu trong các câu trả lời khác nhưng decltype()
dường như có ích khi sử dụng lambda trong một lớp hoặc chức năng templated.
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void boring_template_fn(T t){
auto identity = [](decltype(t) t){ return t;};
std::cout << identity(t) << std::endl;
}
int main(int argc, char *argv[]) {
std::string s("My string");
boring_template_fn(s);
boring_template_fn(1024);
boring_template_fn(true);
}
Bản in:
My string
1024
1
Tôi đã tìm thấy kỹ thuật này hữu ích khi làm việc với mã templated nhưng nhận ra nó vẫn có nghĩa là bản thân lambdas không thể được tạo khuôn.
T
sẽ làm việc tốt thay cho decltype(t)
trong ví dụ này.
Trong C ++ 11, các chức năng lambda không thể được tạo khuôn mẫu, nhưng trong phiên bản tiếp theo của Tiêu chuẩn ISO C ++ (thường được gọi là C ++ 14), tính năng này sẽ được giới thiệu. [Nguồn]
Ví dụ sử dụng:
auto get_container_size = [] (auto container) { return container.size(); };
Lưu ý rằng mặc dù cú pháp sử dụng từ khóa auto
, loại trừ sẽ không sử dụng quy tắc auto
khấu trừ loại, mà thay vào đó sử dụng quy tắc khấu trừ đối số mẫu. Cũng xem đề xuất cho các biểu thức lambda chung (và bản cập nhật cho điều này).
auto
khấu trừ loại được xác định cụ thể giống như quy tắc khấu trừ template
hàm.
Tôi biết rằng câu hỏi này là về C ++ 11. Tuy nhiên, đối với những người đã googled và hạ cánh trên trang này, lambdas templated hiện được hỗ trợ trong C ++ 14 và đi với tên Generic Lambdas.
[thông tin] Hầu hết các trình biên dịch phổ biến đều hỗ trợ tính năng này ngay bây giờ. Hỗ trợ Microsoft Visual Studio 2015. Clang hỗ trợ. GCC hỗ trợ.
Tôi tự hỏi những gì về điều này:
template <class something>
inline std::function<void()> templateLamda() {
return [](){ std::cout << something.memberfunc() };
}
Tôi đã sử dụng mã tương tự như thế này, để tạo một mẫu và tự hỏi liệu trình biên dịch sẽ tối ưu hóa chức năng "gói" ra.
Hãy xem Boost.Phoenix cho lambdas đa hình: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html Không yêu cầu C ++ 0x, bởi đường :)
Có một phần mở rộng gcc cho phép các mẫu lambda :
// create the widgets and set the label
base::for_each(_widgets, [] <typename Key_T, typename Widget_T>
(boost::fusion::pair<Key_T, Widget_T*>& pair) -> void {
pair.second = new Widget_T();
pair.second->set_label_str(Key_T::label);
}
);
nơi _widgets
là mộtstd::tuple< fusion::pair<Key_T, Widget_T>... >
Tôi đã chơi với bản clang mới nhất được version 5.0.1
biên dịch với -std=c++17
cờ và bây giờ có một số hỗ trợ tốt cho các tham số loại tự động cho lambdas:
#include <iostream>
#include <vector>
#include <stdexcept>
int main() {
auto slice = [](auto input, int beg, int end) {
using T = decltype(input);
const auto size = input.size();
if (beg > size || end > size || beg < 0 || end < 0) {
throw std::out_of_range("beg/end must be between [0, input.size())");
}
if (beg > end) {
throw std::invalid_argument("beg must be less than end");
}
return T(input.begin() + beg, input.begin() + end);
};
auto v = std::vector<int> { 1,2,3,4,5 };
for (auto e : slice(v, 1, 4)) {
std::cout << e << " ";
}
std::cout << std::endl;
}
Đây là một giải pháp liên quan đến việc bọc lamba trong một cấu trúc:
template <typename T>
struct LamT
{
static void Go()
{
auto lam = []()
{
T var;
std::cout << "lam, type = " << typeid(var).name() << std::endl;
};
lam();
}
};
Để sử dụng làm:
LamT<int>::Go();
LamT<char>::Go();
#This prints
lam, type = i
lam, type = c
Vấn đề chính với điều này (ngoài việc gõ thêm), bạn không thể nhúng định nghĩa cấu trúc này bên trong một phương thức khác hoặc bạn nhận được (gcc 4.9)
error: a template declaration cannot appear at block scope
Tôi cũng đã thử làm điều này:
template <typename T> using LamdaT = decltype(
[](void)
{
std::cout << "LambT type = " << typeid(T).name() << std::endl;
});
Với hy vọng tôi có thể sử dụng nó như thế này:
LamdaT<int>();
LamdaT<char>();
Nhưng tôi gặp lỗi trình biên dịch:
error: lambda-expression in unevaluated context
Vì vậy, điều này không hoạt động ... nhưng ngay cả khi nó đã biên dịch, nó sẽ bị hạn chế sử dụng vì chúng ta vẫn phải đặt "sử dụng LamdaT" trong phạm vi tệp (vì đó là một mẫu) nhằm đánh bại mục đích của thịt cừu
Tôi không chắc tại sao không ai khác đề xuất điều này, nhưng bạn có thể viết một hàm templated trả về các hàm lambda. Sau đây đã giải quyết vấn đề của tôi, lý do tôi đến trang này:
template <typename DATUM>
std::function<double(DATUM)> makeUnweighted() {
return [](DATUM datum){return 1.0;};
}
Bây giờ bất cứ khi nào tôi muốn một hàm có một loại đối số nhất định (ví dụ std::string
), tôi chỉ cần nói
auto f = makeUnweighted<std::string>()
và bây giờ f("any string")
trở lại 1.0
.
Đó là một ví dụ về những gì tôi muốn nói bởi "hàm lambda templated." (Trường hợp cụ thể này được sử dụng để tự động cung cấp chức năng cân trơ khi ai đó không muốn cân dữ liệu của họ, bất kể dữ liệu của họ có thể là gì.)