2013-02-13 30 views
16

Các mã sau đây đã được biên soạn với VC++ 2012:Quy ước gọi điện mặc định của hàm lambda C++ là gì?

void f1(void (__stdcall *)()) 
{} 

void f2(void (__cdecl *)()) 
{} 

void __cdecl h1() 
{} 

void __stdcall h2() 
{} 

int main() 
{ 
    f1(h1); // error C2664 
    f2(h2); // error C2664 

    f1([](){}); // OK 
    f2([](){}); // OK 

    auto fn = [](){}; 

    f1(fn); // OK 
    f2(fn); // OK 
} 

Tôi nghĩ rằng lỗi là bình thường nhưng các OKS là không bình thường.

Vì vậy, câu hỏi của tôi là:

  1. quy ước gọi của một hàm C++ lambda là gì?

  2. Cách chỉ định quy ước gọi điện của hàm lambda C++?

  3. Nếu quy ước gọi không được xác định, cách tái chế đúng không gian ngăn xếp sau khi đã gọi hàm lambda?

  4. Trình biên dịch có tự động tạo nhiều phiên bản của hàm lambda không? tức là như mã giả sau:

    [] __stdcall() {};

    [] __cdecl() {}; vv

+3

Liên kết câu hỏi này có vẻ hữu ích: http://stackoverflow.com/questions/14169295/how-to-specify-vc11-lambda-calling-convention – jogojapan

+0

'f1' đang sử dụng' __stdcall' nhưng 'h1' đang sử dụng' __cdecl'; nếu bạn trao đổi những thứ xung quanh nó có hoạt động không? – congusbongus

+0

@Cong, Các lỗi là bình thường và OK là bất thường. – xmllmx

Trả lời

14

On VC++ 2012, trình biên dịch chọn tự động gọi chuyển đổi cho lambdas stateless (mà không có biến chụp) khi bạn chuyển đổi "lambda stateless thực hiện chức năng con trỏ".

MSDN C++11 Features:

Lambdas

[...] Ngoài ra trong Visual C++ trong Visual Studio 2012, lambdas stateless là chuyển đổi chức năng con trỏ. [...] (Visual C++ trong Visual Studio 2012 thậm chí còn tốt hơn thế, bởi vì chúng tôi đã chuyển đổi lambdas không trạng thái thành con trỏ hàm có quy ước gọi tùy ý. Điều này rất quan trọng khi bạn đang sử dụng API mong đợi những thứ như hàm __stdcall con trỏ)


EDITED:.

NB: Việc quy đổi gọi là ra khỏi C++ Standard, nó phụ thuộc vào đặc điểm kỹ thuật khác như nền tảng ABI (giao diện nhị phân ứng dụng).

Các câu trả lời sau đây dựa trên mã lắp ráp đầu ra với /FAs compiler option. Vì vậy, nó chỉ là một đoán, và xin vui lòng yêu cầu Microsoft để biết thêm chi tiết; P

Q1. Quy ước gọi hàm lambda C++ là gì?

Q3. Nếu quy ước gọi không được xác định, làm thế nào để tái chế đúng không gian ngăn xếp sau khi đã gọi hàm lambda?

Trước hết, C++ lambda (-expression) KHÔNG phải là một chức năng (hay con trỏ chức năng), bạn có thể gọi operator() để đối tượng lambda như một chức năng bình thường gọi. Và mã lắp ráp đầu ra nói rằng VC++ 2012 tạo ra lambda-body với chuyển đổi cuộc gọi __thiscall.

Q2. Làm thế nào để xác định quy ước gọi của hàm lambda C++?

AFAIK, không có cách nào. (Chỉ có thể là __thiscall)

Q4. Trình biên dịch có tự động tạo nhiều phiên bản của hàm lambda không? ví dụ như các pseudo-code sau: [...]

Có lẽ số VC++ 2012 lambda-type chỉ cung cấp một thực hiện lambda-cơ (void operator()()), nhưng cung cấp nhiều "chuyển đổi người dùng định nghĩa chức năng con trỏ "cho mỗi chuyển đổi cuộc gọi (con trỏ hàm trả về toán tử với void (__fastcall*)(void), void (__stdcall*)(void)void (__cdecl*)(void) loại).

Dưới đây là ví dụ;

// input source code 
auto lm = [](){ /*lambda-body*/ }; 

// reversed C++ code from VC++2012 output assembly code 
class lambda_UNIQUE_HASH { 
    void __thiscall operator()() { 
    /* lambda-body */ 
    } 
    // user-defined conversions 
    typedef void (__fastcall * fp_fastcall_t)(); 
    typedef void (__stdcall * fp_stdcall_t)(); 
    typedef void (__cdecl * fp_cdecl_t)(); 
    operator fp_fastcall_t() { ... } 
    operator fp_stdcall_t() { ... } 
    operator fp_cdecl_t() { ... } 
}; 
lambda_UNIQUE_HASH lm; 
+2

+1 để tham khảo (mặc dù câu trả lời không bao gồm tất cả các khía cạnh của câu hỏi). – jogojapan

+5

@jogojapan: Nó khá nhiều bao gồm tất cả mọi thứ, kể từ khi một chức năng lambda vẫn là một chức năng thành viên cuối cùng. Và một chức năng thành viên không thực sự có các quy ước gọi điện. Không theo cách '__cdecl'. Tuy nhiên, do các quy ước gọi là nền tảng cụ thể, điều đó tùy thuộc vào từng nền tảng để quyết định cách hoạt động của nền tảng đó. Microsoft, hiển thị một mức độ * gây sốc * về thẩm quyền, quyết định theo cách hữu ích nhất. –

+0

@NicolBolas Ý tôi là chủ yếu là văn bản được trích dẫn dường như không cung cấp câu trả lời rõ ràng cho phần 2 của câu hỏi. Các mô tả ở trên nói rằng lambda là theo mặc định chuyển đổi cho tất cả các công ước gọi điện thoại, nhưng điều đó không nhất thiết ngụ ý rằng không có cách nào để xác định quy ước nếu một trong những muốn. Nó sẽ là cú pháp VC cụ thể, tất nhiên. (Tôi không chắc liệu tôi có hiểu điểm của bạn về các chức năng của thành viên không. Lambdas luôn là chức năng của thành viên .. tại sao vậy?) – jogojapan

3

Hàm lambda không trạng thái vẫn là một lớp, nhưng một lớp có thể được chuyển đổi hoàn toàn thành con trỏ hàm.

Tiêu chuẩn C++ không bao gồm quy ước cuộc gọi, nhưng có rất ít lý do tại sao lambda không trạng thái không thể tạo trình bao trong bất kỳ quy ước gọi nào chuyển tiếp tới lambda không trạng thái khi lambda được chuyển thành con trỏ hàm.

Như một ví dụ, chúng ta có thể làm điều này:

#include <iostream> 

void __cdecl h1() {} 
void __stdcall h2(){} 

// I'm lazy: 
typedef decltype(&h1) cdecl_nullary_ptr; 
typedef decltype(&h2) stdcall_nullary_ptr; 

template<typename StatelessNullaryFunctor> 
struct make_cdecl { 
    static void __cdecl do_it() { 
    StatelessNullaryFunctor()(); 
    } 
}; 
template<typename StatelessNullaryFunctor> 
struct make_stdcall { 
    static void __stdcall do_it() { 
    StatelessNullaryFunctor()(); 
    } 
}; 

struct test { 
    void operator()() const { hidden_implementation(); } 

    operator cdecl_nullary_ptr() const { 
    return &make_cdecl<test>::do_it; 
    } 
    operator stdcall_nullary_ptr() const { 
    return &make_stdcall<test>::do_it; 
    } 
}; 

nơi test stateless lớp nullary của chúng tôi có thể được chuyển đổi thành cả một con trỏ cdeclstdcall chức năng ngầm.

Phần quan trọng của điều này là quy ước gọi là một phần của loại con trỏ hàm, vì vậy operator function_type biết quy ước cuộc gọi nào đang được yêu cầu. Và với sự chuyển tiếp hoàn hảo, những điều trên thậm chí có thể hiệu quả.

+0

Hàm lambda là một lớp? – jogojapan

+4

@jogojapan: Có lẽ nên nói, lambda không quốc tịch vẫn là một đối tượng *, nhưng một đối tượng có thể được chuyển đổi thành một con trỏ hàm. –

Các vấn đề liên quan