Loại lambda là gì khi suy ra với Tự động hóa trong C ++ 11?


141

Tôi đã có một nhận thức rằng, loại lambda là một con trỏ hàm. Khi tôi thực hiện kiểm tra sau, tôi thấy nó sai ( bản demo ).

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

Là mã trên thiếu bất kỳ điểm nào? Nếu không thì typeofbiểu thức lambda khi suy ra với autotừ khóa là gì?


8
Kiểu của lambda là một con trỏ hàm hàm - đó là không hiệu quả và bỏ lỡ toàn bộ điểm của lambdas.
Konrad Rudolph

Câu trả lời:


144

Loại biểu thức lambda là không xác định.

Nhưng chúng thường chỉ là đường cú pháp cho functor. Một lambda được dịch trực tiếp thành một functor. Bất cứ thứ gì bên trong []đều được biến thành các tham số của hàm tạo và các thành viên của đối tượng functor và các tham số bên trong ()được biến thành các tham số cho functor operator().

Một lambda không có biến (không có gì bên trong []) có thể được chuyển đổi thành một con trỏ hàm (MSVC2010 không hỗ trợ điều này, nếu đó là trình biên dịch của bạn, nhưng chuyển đổi này là một phần của tiêu chuẩn).

Nhưng loại thực tế của lambda không phải là một con trỏ hàm. Đó là một số loại functor không xác định.


1
MSVC2010 không hỗ trợ chuyển đổi sang con trỏ hàm, nhưng MSVC11 thì có. blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
KindDragon

17
+1 cho "đường cú pháp đơn thuần cho functor." Nhiều sự nhầm lẫn tiềm năng có thể tránh được bằng cách ghi nhớ điều này.
Ben

4
một functor là bất cứ thứ gì với operator()về cơ bản stackoverflow.com/questions3536950/c-functor-and-their-uses
TankorSmash

107

Nó là một cấu trúc không tên duy nhất làm quá tải toán tử gọi hàm. Mỗi phiên bản của lambda giới thiệu một loại mới.

Trong trường hợp đặc biệt của lambda không bắt, cấu trúc ngoài ra có một chuyển đổi ngầm định thành một con trỏ hàm.


2
Câu trả lời tốt đẹp. Chính xác hơn nhiều so với của tôi. 1 :)
jalf

4
+1 cho phần độc nhất, ban đầu nó rất đáng ngạc nhiên và đáng chú ý.
Matthieu M.

Không phải là nó thực sự quan trọng, nhưng là loại thực sự không được đặt tên, hoặc nó chỉ không được đặt tên cho đến thời gian biên dịch? IOW, người ta có thể sử dụng RTTI để tìm tên trình biên dịch đã quyết định không?
Ben

3
@Ben, nó không được đặt tên và theo như ngôn ngữ C ++ có liên quan, không có thứ gọi là "tên mà trình biên dịch quyết định". Kết quả của type_info::name()được xác định theo thực hiện, vì vậy nó có thể trả về bất cứ điều gì. Trong thực tế, trình biên dịch sẽ đặt tên cho loại vì lợi ích của trình liên kết.
avakar

1
Gần đây, khi được hỏi câu hỏi này, tôi thường nói rằng loại lambda tên, trình biên dịch biết nó, nó chỉ không thể nói được.
Andre Kostur

24

[C++11: 5.1.2/3]: Loại biểu thức lambda (cũng là loại đối tượng đóng) là một loại lớp không liên kết duy nhất, chưa được đặt tên - được gọi là loại đóng - có các thuộc tính được mô tả bên dưới. Loại lớp này không phải là tổng hợp (8.5.1). Kiểu đóng được khai báo trong phạm vi khối nhỏ nhất, phạm vi lớp hoặc phạm vi không gian tên có chứa biểu thức lambda tương ứng . [..]

Mệnh đề đi vào danh sách các thuộc tính khác nhau của loại này. Dưới đây là một số điểm nổi bật:

[C++11: 5.1.2/5]:Kiểu đóng cửa cho một lambda thể hiện có một công inlinehàm operator gọi (13.5.4) có các tham số và kiểu trả về được mô tả bởi các lambda-biểu hiện của tham số-khai-khoảntrailing-trở-type tương ứng. [..]

[C++11: 5.1.2/6]:Kiểu đóng cho biểu thức lambda không có lambda-Capture có hàm chuyển đổi const không ảo không công khai thành con trỏ thành hàm có cùng tham số và trả về kiểu như toán tử gọi hàm của kiểu đóng. Giá trị được trả về bởi hàm chuyển đổi này sẽ là địa chỉ của hàm, khi được gọi, có tác dụng tương tự như gọi toán tử gọi hàm của kiểu đóng.

Hậu quả của đoạn văn cuối cùng này là, nếu bạn đã sử dụng một chuyển đổi, bạn sẽ có thể gán LAMBDAcho pFptr.


3
#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

Các loại chức năng thực sự giống nhau, nhưng lambda giới thiệu loại mới (như functor).


Tôi khuyên bạn nên sử dụng phương pháp tháo gỡ CXXABI nếu bạn đã đi theo lộ trình này. Thay vào đó, tôi thường tận dụng __PRETTY_FUNCTION__, như trong template<class T> const char* pretty(T && t) { return __PRETTY_FUNCTION__; }và lột bỏ phần phụ nếu nó bắt đầu trở nên đông đúc. Tôi thích xem các bước được hiển thị trong thay thế mẫu. Nếu bạn bị thiếu __PRETTY_FUNCTION__, có các lựa chọn thay thế cho MSVC, v.v., nhưng kết quả luôn phụ thuộc vào trình biên dịch vì cùng lý do CXXABI là cần thiết.
John P

1

Cũng cần lưu ý rằng lambda có thể chuyển đổi thành con trỏ hàm. Tuy nhiên, typeid <> trả về một đối tượng không phải là đối tượng khác với lambda với con trỏ hàm chung. Vì vậy, bài kiểm tra cho typeid <> không phải là một giả định hợp lệ. Nói chung, C ++ 11 không muốn chúng tôi lo lắng về đặc tả loại, tất cả đều quan trọng nếu một loại nhất định có thể chuyển đổi thành loại mục tiêu.


Điều đó công bằng, nhưng các loại in đi một chặng đường dài để đi đến đúng loại, chưa kể đến việc bắt các trường hợp loại có thể chuyển đổi nhưng không thỏa mãn các ràng buộc khác. (Tôi sẽ luôn luôn hướng tới các ràng buộc "thống nhất" bất cứ khi nào có thể, nhưng ai đó đang cố gắng làm điều đó có nhiều lý do hơn để thể hiện công việc của họ trong quá trình phát triển.)
John P

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.