2015-03-05 20 views
6

Tôi có một lớp tiện ích:Khởi tạo và lambda kiểu lập luận

struct Atreturn 
{ 
    std::function<void()> funcdestr; 
    Atreturn(std::function<void()> fd): funcdestr(fd) {} 
    ~Atreturn() { funcdestr(); } 
}; 

Lưu ý không explicit thuộc tính tại nhà xây dựng.

có thể sử dụng nên:

  1. trực tiếp khởi tạo constructor gọi:

    Atreturn hook ([something]() { DestroySomething(something); }); 
    
  2. Copy-khởi tạo constructor gọi:

    Atreturn hook = [something]() { DestroySomething(something); }; 
    
  3. Direct-list-khởi tạo cuộc gọi constructor:

    Atreturn hook { [something]() { DestroySomething(something); }}; 
    

Bây giờ câu hỏi: kiến ​​thức tốt nhất của tôi, phương pháp # 1 và # 2 nên được cho phép vì họ là về mặt lý thuyết điều tương tự, với điều kiện là không có explicit tại các nhà xây dựng, trong khi # 3 không nên được cho phép bởi vì cú pháp này ngăn chặn chuyển đổi (ít nhất nó là như vậy cho int nếu bạn đã cố gắng int{2.1}).

Tuy nhiên, gcc 4.9 cho phép phương pháp # 1 và # 3, nhưng không cho phép # 2 (và nói conversion from '...::<lambda()>' to non-scalar 'Atreturn' type requested). Điều này nghe có vẻ điên rồ bởi vì nó thường xảy ra chỉ khi bạn có một nhà xây dựng explicit. Ai có thể giải thích, tại sao?

Ngoài ra, hãy để tôi làm cho vấn đề này rõ ràng hơn: Tôi cần một số cú pháp không quá vụng về để khởi tạo đối tượng Atreturn này, ít nhất là không cần thêm dấu ngoặc đơn hoặc dấu ngoặc đơn. Vấn đề là các trình soạn thảo có chức năng tự động thụt lề có vấn đề với sự sắp xếp hợp lý khi đối số là C++ 11 lambda. Vì vậy, tôi cần một số cú pháp có thể được diễn tả như:

Atreturn BLAH BLAH BLAH [something]() { DestroySomething(something); }; 
+1

(Cũng thêm bản dựng khởi tạo bản sao-danh sách: 'Móc móc Areturn = {...}'?) –

+0

Giống như phương pháp # 3, đây = tùy chọn trong C++ 11. – Ethouris

+0

# 1 và # 2 là * không * giống nhau. Họ sẽ là nếu loại bên phải là 'Atreturn', nhưng nó không phải. – Angew

Trả lời

7

khi # 3 không nên được cho phép bởi vì cú pháp này ngăn chặn chuyển đổi (ít nhất nó là như vậy cho int nếu bạn đã cố gắng int {2,1}).

Điều đó không đúng. Quy tắc là không cho phép thu hẹp chuyển đổi. Các loại chuyển đổi khác được cho phép. int{2.1} là một chuyển đổi thu hẹp, bởi vì nó thay đổi giá trị, mất độ chính xác. int{2.0} không phải là một chuyển đổi thu hẹp, bởi vì giá trị không thay đổi.

Lý do số 2 thất bại là yêu cầu hai chuyển đổi ẩn do người dùng xác định, bị cấm.

Về mặt lý thuyết, một bản sao-khởi như:

Atreturn hook = []() {}; 

tương đương với:

Atreturn hook = Atreturn([]() {}); 

(ngoại trừ việc nó không thể gọi 'rõ ràng' nhà thầu, và trình biên dịch được phép bõ mẫu âm chót các sao chép).

Điều này có nghĩa là trước tiên lambda sẽ phải chuyển đổi hoàn toàn thành function<void()> và sau đó điều đó sẽ phải chuyển đổi hoàn toàn thành Atreturn. Cả hai chuyển đổi đó là "chuỗi chuyển đổi do người dùng xác định" có nghĩa là họ gọi một hàm tạo, thay vì các chuyển đổi được tích hợp như int thành long và tiêu chuẩn cho biết chuỗi chuyển đổi tiềm ẩn không thể bao gồm nhiều chuyển đổi do người dùng xác định.

Vấn đề là thực sự không liên quan đến lambdas, bạn có thể chứng minh một cách chính xác các lỗi tương tự như thế này:

struct L { }; 
struct F { F(L) { } }; 
struct A { A(F) { } }; 
A a = L(); 

l.cc:4:9: error: conversion from ‘L’ to non-scalar type ‘A’ requested 
A a = L(); 
     ^

Một lần nữa, vấn đề là việc chuyển đổi chuỗi ngầm L -> F -> A liên quan đến hai chuyển đổi người dùng định nghĩa, mà bị cấm .

Tôi không có nhiều thông cảm cho vấn đề của bạn muốn điều chỉnh mã để giúp tự động thụt đầu dòng - mã không được xáo trộn để phù hợp với trình chỉnh sửa thiếu sót. Tuy nhiên, một tùy chọn khác là thêm một hàm tạo mẫu chấp nhận bất kỳ thứ gì có thể được chuyển đổi thành std::function<void()> ví dụ:

struct Atreturn 
{ 
    using func_type = std::function<void()>; 
    template<typename T, 
      typename Requires = decltype(func_type(std::declval<T&&>())> 
    Atreturn(T t) : funcdestr(std::move(t)) { } 
    ... 
}; 

này sẽ cho phép các lambda được chuyển đổi trực tiếp với Atreturn, mà không đòi hỏi một chuyển đổi ngầm để function<void()> đầu tiên.

+0

Lambda để 'function ' là khá ok, nhưng nơi nào bạn thấy sự cần thiết phải chuyển đổi đó để 'Atreturn'? Không nên khởi tạo với dấu bằng bằng cách sử dụng constructor 1-đối số với tham số của loại đó? – Ethouris

+3

@Ethouris Không, khởi tạo trực tiếp (không có '=') và sao chép-khởi tạo (với '=') là * không * giống nhau. Cái thứ hai yêu cầu tạo tạm thời và sử dụng nó làm đối số cho ctor sao chép (ngay cả khi nó được ưu tiên trong thực tế). – Angew

+0

@Ethouris, tôi đã cập nhật câu trả lời để giải thích điều đó. Ở phía bên phải của '=' các quy tắc ngôn ngữ nói rằng có một đối tượng 'Atreturn' tạm thời. Ngay cả khi tạm thời đó được tách ra, các quy tắc về quá tải và chuyển đổi phải hoạt động như thể tạm thời được tạo ra. –

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