2014-07-03 18 views
15

Các (có thể là không phải C++ 14, có lẽ Thư viện TS) cơ sở make_optional được định nghĩa (in n3672) như:Tại sao make_optional phân rã loại đối số của nó?

template <class T> 
    constexpr optional<typename decay<T>::type> make_optional(T&& v) { 
    return optional<typename decay<T>::type>(std::forward<T>(v)); 
    } 

Tại sao nó cần thiết để chuyển đổi các loại T (tức là không chỉ trở optional<T>), và Liệu có một lý lẽ triết học (cũng như thực tế) để sử dụng decay cụ thể là sự chuyển đổi?

+0

Bởi vì T có thể là một tài liệu tham khảo giá trị trái, và tài liệu tham khảo bắt buộc không được phép. – Simple

+1

@ Đơn giản vậy tại sao không chỉ 'remove_reference'? – ecatmur

+1

Có lẽ vì vậy bạn không thể tạo một 'tùy chọn ' hoặc 'tùy chọn ', cũng sẽ có ngữ nghĩa kỳ lạ. – Simple

Trả lời

16

Mục đích chung là decay là lấy một loại và sửa đổi nó để phù hợp để lưu trữ.

Hãy xem những ví dụ mà decay làm cho công việc, trong khi remove_reference sẽ không:

auto foo(std::string const& s) { 
    if (global_condition) 
    return make_optional(s); 
    else 
    return {}; 
} 

hoặc

void function() { std::cout << "hello world!\n"; } 
auto bar() { return std::make_optional(function); } 

hoặc

int buff[15]; 
auto baz() { return std::make_optional(buff); } 

Một optional<int[15]> sẽ là một rất lạ thú - các mảng kiểu C không hoạt động tốt khi được xử lý như li terals, đó là những gì optional thực hiện tham số T.

Nếu bạn đang làm cho một bản sao của dữ liệu, const hoặc volatile bản chất của nguồn không quan trọng. Và bạn chỉ có thể tạo bản sao mảng và chức năng đơn giản bằng cách phân rã chúng thành con trỏ (không bị rơi trở lại trên std::array hoặc tương tự). (Về mặt lý thuyết, công việc có thể được thực hiện để làm optional<int[15]> làm việc, nhưng nó sẽ là rất nhiều biến chứng thêm)

Vì vậy std::decay giải quyết tất cả những vấn đề này, và không thực sự gây ra vấn đề, miễn là bạn cho phép make_optional để suy luận của mình loại đối số thay vì truyền T theo nghĩa đen.

Nếu bạn muốn chuyển một số T theo nghĩa đen, không có lý do gì để sử dụng make_optional sau khi tất cả.

+1

Tôi chỉ tìm thấy http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2006/n2069.html hỗ trợ khẳng định của bạn rằng mục đích của (a) của 'phân rã' là" lấy một gõ và sửa đổi nó để phù hợp với lưu trữ ". Thật không may là mục đích đó không được thể hiện rõ ràng hơn trong tiêu chuẩn. – ecatmur

6

Chỉ cần nhìn vào những gì decay làm:

  • Loại bỏ tham khảo qualifiers- T&T; không có tham chiếu tùy chọn, nếu không bạn không thể bán lại số optional, cần phải được chuyển nhượng.

  • Đối với loại mảng, loại bỏ phạm vi— T[42]T*; các mảng phạm vi cố định tùy chọn không hữu ích vì mỗi kích thước mảng có một loại khác nhau và bạn không thể chuyển trực tiếp các loại mảng theo giá trị, cần thiết cho các chức năng thành viên valuevalue_or hoạt động.

  • Đối với các loại chức năng, hãy thêm con trỏ— T(U)T(*)(U); không có tham chiếu hàm tùy chọn, vì các lý do tương tự.

  • Nếu không, hãy xóa cv-vòng loại— const intint; không có giá trị tùy chọn const, một lần nữa, nếu không bạn không thể thay đổi lại số optional.

+0

Hãy thử thực hiện 'tùy chọn ' công việc - nó rất phức tạp, vì các mảng không thích được sao chép xung quanh bên ngoài 'struct'. – Yakk

+0

Tôi không hiểu điểm số 2. –

+0

Trên # 1, các tham chiếu tùy chọn tồn tại trong đặc điểm kỹ thuật này, như các chỉ dẫn cơ bản: [tham chiếu triển khai] (https://github.com/akrzemi1/Optional/blob/49f29c53d3f7b117cc33277e52b0a5b03a44570c/optional.hpp#L605). Họ có thể được phân công lại; xem [ghi chú về lý do tại sao nó được quyết định làm như vậy] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3527.html#optional_ref.rationale.assign). Mặc dù chỉ vì bạn * có thể * có một tham chiếu tùy chọn không làm cho nó trở thành mặc định tốt cho 'make_optional', theo câu trả lời của @ Brian vì sao đây là các quy ước sau trong' make_tuple' và 'make_pair'. – HostileFork

13

Đây không phải là hành vi mới với make_optional; các chức năng tiện ích make_pairmake_tuple hoạt động theo cùng một cách. Tôi có thể thấy ít nhất hai lý do để thực hiện việc này:

  1. Có thể không mong muốn hoặc không thể thực hiện mẫu với các loại chưa hoàn tất.

    • Nếu T là một loại chức năng, tốt, bạn chỉ cần không thể cửa hàng một hàm bên trong một lớp học, thời kỳ; nhưng bạn có thể lưu trữ một con trỏ hàm (kiểu bị phân rã).

    • Nếu T là một loại mảng: lớp kết quả sẽ là "khiếm khuyết" bởi vì nó không thể được sao chép, do thực tế là các mảng không được sao chép chuyển nhượng. Đối với optional, không thể biên dịch tất cả hàm thành viên value_or vì nó trả về T. Do đó, bạn hoàn toàn không thể có loại mảng trong một số optional.

  2. Không phân rã các loại có thể dẫn đến hành vi không mong muốn.

    • Nếu đối số là một chuỗi chữ , cá nhân tôi mong chờ các kiểu dữ liệu được const char* hơn const char [n]. Sự phân rã này xảy ra ở hầu hết các nơi, vậy tại sao không ở đây?

    • Nếu v là một lvalue thì T sẽ được khấu trừ dưới dạng loại tham chiếu giá trị. Tôi thực sự muốn một cặp có chứa một tham chiếu, ví dụ, chỉ vì một trong các đối số là một lvalue? Chắc chắn là không.

    • Loại bên trong cặp hoặc tuple hoặc tùy chọn hoặc bất kỳ thứ gì không được nhận chứng chỉ cv của v. Tức là, chúng tôi có x được khai báo là const int. Nên make_optional(x) tạo một optional<const int>? Không, không nên; nó sẽ tạo ra optional<int>.

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